2014년 11월 25일 화요일

[Android] 카메라 연동 & 사진 자르기(CROP) [출처] [Android] 카메라 연동 & 사진 자르기(CROP)|작성자 LifeClue

펌위치 : http://blog.naver.com/legendx/40132435162

본 글은 안드로이드 커뮤니티 안드로이드펍에 먼저 게재되었음을 밝힌다.

안드로이드는 폰의 다양성으로 인해서 같은 소스로도 다른 결과물을 내놓는 경우가 좀 있다.
카메라 액티비티를 이용할 때도 예외는 없었는데, 그 이유를 유추해보자면, 각 사에서 인터페이스를 각자 만들기 때문으로 분석된다.
본인의 폰 갤럭시S로 개발하면서 유추해본 삼성의 카메라 액티비티 작동 순서는 다음과 같다.
인텐트 전달 > 인텐트를 토대로 액티비티 설정 > 카메라 준비 > 촬영 대기 > 촬영 > 방향 확인 > 방향 수정 후 저장 대기 > 저장 > 파일로 저장 > DB에 저장 > Uri 또는 Bitmap 반환
그렇다면 우리는 반환되는 Uri 또는 Bitmap 객체를 사용하면 될 것이다.
그러나 이 반환하는 과정에서도 각 사마다 다르다. 제대로된 값을 넘겨주지 않는 경우가 허다하다.
이는 안드로이드펍의 단영님께서 작성하신 글에서 참고할 수 있다.

그리고, 같은 글의 청바지님 댓글에서 좀 더 호환성이 좋게 작동할 수 있는 실마리를 찾을 수 있었다.
바로 DB에 저장한 그것이다.
안드로이드는 미디어 탐색을 통해서 직접 폴더>파일을 찾아들어가지 않아도 음악이나 사진을 쉽게 볼 수 있는 것이 특징이다.
(노래를 넣고 재생기를 실행해보면 추가한 노래가 목록에 바로 떠있음을 알 수 있다.)
이는 자체 DB에 미디어에 대한 정보를 담아놓기 때문인데, 청바지님의 댓글은 이 곳에 가장 최근에 추가된 미디어의 경로를 가져온다는 것이다.
이를 토대로 소스를 작성해보자.

아래 소스는 카메라 액티비티를 실행시키는 소스다.
인텐트를 생성할 때 액션을 이용해서 건네는 것이다.
MediaStor.ACTION_IMAGE_CAPTURE 액션은 바로 카메라 촬영을 위한 액션이다.
반환값을 받기 위해 startActivityForResult()로 넘겨준다. CAMERA는 본인이 만든 상수로, 어떤 액티비티에서 반환값이 왔는지를
식별하기 위한 식별값이다. 각자가 편한 값을 적어주면 되겠다. (되도록 정수로..)
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent,CAMERA);
/* CAMERA는 상수로서, onActivityResult에서 requestCode로 돌려받을 때 사용되는 정수값이다. 단순히 구분을 위함이니 여러분들 마음대로 사용하시면 되겠다.
무엇을 구분하는고, 하면 사용자가 선택한 이미지가 카메라 촬영으로 된 것인지, 갤러리에서 선택한 것인지를 판단한다.*/
그리고 바로 아래 소스를 작성하는데, 이는 액티비티에서 반환하는 값을 받기 위한 메서드로,
Override 하면 되겠다.
onActivityResult()에서 requestCode는 startActivityForResult()에서 두 번째로 넘긴 값이며,
이 값으로 반환값을 넘긴 액티비티를 식별한다.
resultCode는 액티비에서 정상적으로 반환값을 넘겼는지, 아니면 사용자에 의해 과정이 취소되었는지를 알 수 있다.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    Intent intent;
    File toFile = null; // 임시파일을 복사할 경로를 지정하기 위해 선언
    File fromFile = null; // 불러올 파일의 경로를 저장하기 위해 선언
    // 임시파일을 복사할 때 원본 파일을 읽어오기 위해 선언
    FileInputStream inputStream = null;
    // 임시파일을 복사할 때 원본 파일을 임시파일에 쓰기 위해 선언
    FileOutputStream outputStream = null;
    // 여기서 아까 인텐트를 부를 때 보냈던 상수가 requestCode에 실려 되돌아온다.
    switch (requestCode) { 
    case CAMERA:
        // 만약 사용자가 촬영을 안했거나 다른 이유에 의해 인텐트가 예기치
        // 않게 종료되었을 경우 아래의 과정을 무시한채 종료한다.
        if (resultCode == RESULT_CANCELED) return; 
        if (cropedImageUri != null) {
            // 만약 복사하려는 파일이 존재하면 삭제한다. (임시파일이 쌓이는 것을 방지)
            // 이 것은 본인 선택이다. 임시파일의 경로가 상수라면 상관 없을지도 모른다.
            // 본인은 실행할 때마다 임시파일의 파일명이 바뀌기에 삭제한다.
            File f = new File(cropedImageUri.getPath());
            if (f.exists()) {
                f.delete();
            }
        }
        // 넥서스S에서 Uri를 제대로 얻어오질 못하는데
        // 이 부분을 수정하면 가능 할지도 모르리라 의심만 해본다.
        // 우리가 찾고자 하는 마지막에 등록된 레코드가 있을 테이블이다.
        Uri uriImages = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        int id = -1; //촬영된 이미지의 DB에서의 ID를 저장할 변수다.
 
        //아래는 DB의 해당 테이블에서 조건에 맞는 레코드의 어떤 필드값을
        //불러올지를 지정하는 것이다.
        //우리는 해당 레코드의 모든 필드값이 필요한 것이 아니기에 한정한다.
        String[] IMAGE_PROJECTION = {
            MediaStore.Images.ImageColumns.DATA, //데이터의 파일 절대 경로(문자열)
            MediaStore.Images.ImageColumns._ID, //데이터의 테이블상의 아이디(정수)
        };
        try {
            // 테이블에서 조건 없이 필드값을 한정해서 불러혼다. 그럼, 갤러리에 보이는
            // 사진이 100개면 Cursor는 100번이나 움직일 수 있다.
            Cursor cursorImages = getContentResolver().query(uriImages, IMAGE_PROJECTION, null, null,null);
            // cursor가 무리없이 열렸고, 마지막으로 이동되었으면 경로를 얻어온다.
            // (마지막 번째에 등록된 이미지의 실 경로를 불러온다는 말이다.
            if (cursorImages != null) {
                if (cursorImages.moveToLast()) {
                    fromFile = new File(cursorImages.getString(0)); //이 것이 경로
                    id = cursorImages.getInt(1); //이 것이 아이디
                    //사실 아이디는 필요 없을지 모르지만, 본인같은 경우는 원본을
                    //남기지 않기 때문에 이 아이디가 필요하다.
                }
                cursorImages.close(); // 커서 사용이 끝나면 꼭 닫아준다.
            }
<br />
        } catch(Exception e) {
            e.printStackTrace();
        }
        // 임시파일의 경로로서, 본인은 원본과 겹치지 않게 아래와 같이 작성하였다.
        toFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg");
 
        try {
            // 이 부분이 파일을 복사하는 부분이다.
            // 이 부분이 없으면 CROP할 때 SK W폰에서 다른 이미지를 불러온다.
            // 이유는 모르지만 원본이 소실되거나 DB에서 소실되는 지도 모르겠다.
            inputStream = new FileInputStream(fromFile);
            outputStream = new FileOutputStream(toFile);
            FileChannel fcin = inputStream.getChannel();
            FileChannel fcout = outputStream.getChannel();
 
            long size = fcin.size();
 
            fcin.transferTo(0, size, fcout);
 
            fcout.close();
            fcin.close();
            outputStream.close();
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 그리고 원본은 지운다.
        fromFile.delete();
        // DB상에 남아있는 원본 레코드도 지운다. 그렇지 않으면 이미지의 존재기록이 
        // 남아서 갤러리에 검은색 썸네일 또는 망가진 이미지 아이콘의 썸네일이 뜬다. 
        // (미관상 안좋음)
        getBaseContext().getContentResolver().delete(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
            MediaStore.Images.ImageColumns._ID + "=" + id, 
            null
        );
        // 임시파일 경로를 Uri로 변환한다.
        cropedImageUri = Uri.fromFile(toFile);
        // 그리고 크롭을 실행한다.
        intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(cropedImageUri, "image/*");
        // outputX와 outputY는 자를 크기를 정하는 것이다. 
        // 이 때는 자를 영역이 이동만 가능하다.
        // 크기 변경 안됨
        //intent.putExtra("outputX", GlobalData.dip(this, 460));
        //intent.putExtra("outputY", GlobalData.dip(this, 345));
        // aspectX, aspectY는 자를 영역 크기를 조정시 비율을 정해놓는 것이다.
        //intent.putExtra("aspectX", 4);
        //intent.putExtra("aspectY", 3);
        intent.putExtra("scale", true);
        // output의 경로가 임시파일과 같기 때문에 잘린 이미지는 임시파일에 
        // 덮어쓰이게 되겠다.
        intent.putExtra("output", cropedImageUri);
<font class="Apple-style-span" color="#009e25">        // CROP역시 클래스 내에서 임의로 만든 상수이다.</font><br />        startActivityForResult(intent, CROP);<br />        break;<br />    case : CROP<br />        <font class="Apple-style-span" color="#009e25">//...이하 생략</font>

resultCode 는 상수로 사용할 수 있는데,

위 소스가 길고, 보기 어려워 간단하게 설명한다.
switch에 requestCode를 넣고 있는데, 
여기에는 아까 인텐트를 날릴 때 쓴 CAMERA라는 상수값이 들어있다.
이 switch에는 두 개의 case가 들어있는데, 하나는 CAMERA, 하나는 CROP이다.
우선은 사진을 찍을 것이니, CAMERA case문으로 진입할 것이다.

아래는 DB에서 검색을 할 때 어떤 자료를 가져올 깃인지를 결정하는 것으로,
전화번호부에 이름, 생일, 전화번호, 이메일 등이 있을 때
이름과 이메일만 검색하겠다고 정하는 것과 비슷하다.
이 곳에서는 DATA와 _ID만 갖고 검색을 하는데,
사실 하나의 미디어는 이보다 더 많은 자료를 갖고 있다. 이를테면 위도, 경도, 사진 방향 등이다.
하지만 우리는 최근에 등록된 미디어의 파일경로가 필요하기 때문에 두 가지만 있으면 된다.
_ID는 미디어가 등록되는 순으로 번호가 매겨지기 때문에 _ID에서 가장 큰 값으로 검색하면
최근에 등록된 미디어가 나오고, 여기서 DATA에 들어있는 값을 가져오는 것이다. 
String[] IMAGE_PROJECTION = {
MediaStore.Images.ImageColumns.DATA, //데이터의 파일 절대 경로(문자열형) MediaStore.Images.ImageColumns._ID, //데이터의 테이블상의 아이디(정수형)
};
아래 부분은 DB에서 자료를 읽어오는 부분이다.
query() 메서드는 실제로 DB에 쿼리를 날리는 부분이다.
첫 인자는 SQL의 SELECT문을 사용할 때 FROM 뒤에 따라오는 테이블 이름 정도로 생각하면 쉽겠다.
(여기선 uriImages)
두 번째 인자는 SELECT 뒤에 따라오는 참조할 필드명이 되고, (여기선 IMAGE_PROJECTION)
세 번째 인자는 WHERE 뒤에 따라오는 조건문 정도로 보면 된다. (여기선 null)
네 번째 인자는 세 번째 인자가 많을 때 배열로 넣기 위한 부분이고, (여기선 null)
다섯 번째 인자는 정렬을 어떻게 할지를 정해주는 부분이다.
WHERE 이후에 들어가는 부분으로 보면 된다. (여기선 null)
우리는 Image 미디어가 있는 테이블에서 DATA 필드와 _ID필드의 값을 가져오려고
아래와 같은 쿼리를 날렸다.
Cursor cursorImages = getContentResolver().query(uriImages, IMAGE_PROJECTION, null, null, null);
커서가 제대로 넘어왔다면 null이 아닐 것이고, moveToLast() 메서드는 가져온 여러 값 중 가장 마지막으로 이동하라는 메서드인데, 이 메서드가 성공하면 참을 반환한다.
가장 마지막으로 이동하는 이유는 데이터는 DB에 저장될 때 항상 마지막에 저장되기 때문이다.
커서에서 0번째는 파일 경로이고, 1번째는 그 레코드의 ID인데,
이 순서는 위의 IMAGE_PROJECTION에 적은 순서이다.
값을 가져오는 메서드가 다른 이유는 DB에서 필드의 형태가 위와 같이 짜여있기 때문이다.
즉, DATA 필드에는 문자열이 들어있고, _ID 필드에는 정수형 값이 들어있다.
if (cursorImages != null && cursorImages.moveToLast()) {
    fromFile = new File(cursorImages.getString(0)); //이 것이 경로
    id = cursorImages.getInt(1); //이 것이 아이디입니다.
    cursorImages.close(); //커서 사용이 끝나면 꼭 닫아줍시다.
}
Cursor
 DATA
 _ID
 (처음 위치) >>
 file://mnt/sdcard/...
 34

 file://mnt/sdcard/...
 35
 (moveToLast() 후 위치) >>
 file://mnt/sdcard/...
 36

그림으로 표현하자면 위와 같다. 물론 그림은 아니고 표이지만;
위 표를 보면 현재 폰에는 그림이 3개 있다고 볼 수 있다. 이 전 33개의 이미지는 지워졌을 것이다.
그렇다고 하자.
3개의 그림 중 _ID가 36인 이미지가 가장 마지막에 저장된 사진이다. 즉, 막 촬영한 사진이란 뜻이다.
처음 커서는 _ID 34인 레코드(가로로 한 줄을 레코드라고 한다)에 가 있지만 moveToLast()를 통해서
마지막 레코드로 이동할 수 있다.

아래 소스는 저장된 원본 소스를 복사하는 부분이다. 복사본을 사용하지 않을 경우 SK W폰에서
호환성 문제를 일으키게 된다. 자르기로 넘어갈 때 다른 이미지를 불러오는 것이다.
소스를 보면 toFile에 경로를 넣는데,
Environment.getExternalStorageDirectory().getAbsolutePath() 메서드는 사용자가 접근할 수 있는
SD Card의 절대경로이다.
파일 복사는 채널을 이용하는 것이 성능상 제일 좋은데, 이 부분은 서비님의 포스트를 참고하였다.
toFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.jpg");
// ...
inputStream = new FileInputStream(fromFile);
outputStream = new FileOutputStream(toFile);
FileChannel fcin = inputStream.getChannel();
FileChannel fcout = outputStream.getChannel();

long size = fcin.size();

fcin.transferTo(0, size, fcout);

fcout.close();
fcin.close();
outputStream.close();
inputStream.close();
그리고 본인의 경우 촬영된 원본을 남기지 않는 앱의 특성상 원본을 삭제하는데,
이 때 DB의 레코드도 지워줘야지, 안그러면 갤러리 등에서 이미지 깨짐 아이콘으로
이미지의 정보만 떠다니게 된다.
(갤러리에서 검은 썸네일이 떠 있지만 터치해도 열리지 않는다; 이게 엄청 지저분하다;)
fromFile.delete(); 가 원본 파일을 지우는 부분인데, 파일이 없을 경우 예외가 발생하지만
촬영한 사진을 사용자의 상호작용이 없는 과정에서 바로 삭제하기 때문에 예외처리는 생략한다.
두 번째 부분이 DB에서 사진의 레코드를 삭제하는 부분이다.
fromFile.delete();
getBaseContext().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + id, null);
이제 사진 자르기를 해야 한다.
자르기 전에 파일 경로를 Uri로 변환해야 하는데, 이 때 저장할 Uri는 클래스 맴버로 선언하기 바란다.
사진 자르기로 주소를 보내면 해당 파일에 자른 사진을 덮어쓰기 때문이다.
(다른 액티비티에 다녀와도 같은 경로를 사용하기 때문에 맴버로 있으면 편하다.)
cropedImageUri = Uri.fromFile(toFile);
intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(cropedImageUri, "image/*");
//intent.putExtra("outputX", GlobalData.dip(this, 460));
//intent.putExtra("outputY", GlobalData.dip(this, 345));
//intent.putExtra("aspectX", 4);
//intent.putExtra("aspectY", 3);
intent.putExtra("scale", true);
intent.putExtra("output", cropedImageUri);
startActivityForResult(intent, CROP);
startActivityFOrResult에서 requestCode를 CROP으로 보냈으니 다음번에 들어올때는
case CROP : 부분에 걸릴 것이다.
case CROP
    if (resultCode == RESULT_OK) {
        cameraButton.setImageURI(null);
        cameraButton.setImageURI(cropedImageUri);
    }
    else {
        if (cropedImageUri != null) {
            File f = new File(cropedImageUri.getPath());
            if (f.exists()) {
                f.delete();
            }
        }
    }
    break;
}
이 부분은 따로 설명할 것이 없다. CROP에 걸리면 잘린 사진을 개발자 임의로 사용하면 된다.
출력 사진 경로를 복사본 경로와 같게 주었으니, 복사본에 덮어씌워져 있을 것이다.
본인은 버튼 이미지로 활용하였다.

이렇게 카메라 작동과 사진 자르기에 대해 알아보았다.
위 소스로 코딩한 결과 다수의 기기에서 정상작동 하였는데
넥서스S에서는 촬영후 자르기를 하면 자르기에 다른 이미지가 들어와버리고,
아트릭스에서는 촬영후 자르기를 하면 촬영한 사진이 방향 보정이 되어있지 않고, 앱을 끄고 보면
런처의 배경화면이 촬영한 사진으로 바뀌어 있는 문제가 있다.
방향 보정이라 함은 폰을 가로로 찍었으면 돌아왔을 때 사진이 세워져 있어야 하는데 누운 그대로 있다는 뜻이다.

그 외에는 아직 발견한 문제가 없다. 그러나 넥서스S와 아트릭스 테스트폰이 없는 관계로 위 문제의 해결책은 아직 알 수 없다.

전체소스를 요청하시는 분들이 많아 포스트에 붙인다.
소스에 설명을 자세히 붙여야 하는데 시간이 없어 우선 소스만 첨부하니
필요하신 분들은 참고하시라
P.S. 소스를 함수로 묶고 변수도 손질해야 더 보시기 쉬우실텐데 원본 소스 그대로 올린 점 양해 부탁드린다.

public class Camera extends Activity {
private Uri cropedImageUri;
private ImageButton cameraButton;
static final int CAMERA = 1;
static final int ALBUM = 2;
static final int CHOOSER = 3;
static final int CROP = 4;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(<wbr />savedInstanceState);
setContentView(R.layout.<wbr />camera);
cropedImageUri = null;
        
cameraButton = (ImageButton) findViewById(R.id.camera);
cameraButton.<wbr />setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// showDialog()는 액티비티의 메서드이며, 
// 호출시 아래 재정의한 onCreateDialog()가 호출됩니다.
showDialog(0);
}
});
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<< Add Photo >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@Override
protected Dialog onCreateDialog(int id) {
// 이 메서드는 단독선택 다이얼로그를 생성하여 화면에 표시합니다.
// 아래 switch case 문을 보시면 0번은 촬영, 1번은 갤러리, 2번은 다른 앱 에서
// 이미지를 얻습니다.
// R.array.pick_image는 다이얼로그가 뜰 때 선택할 수 있는 항목들의 이름입니다.
// 저는 {촬영하여 선택, 갤러리에서 선택, 앱에서 선택} 이렇게 세 가지가 있습니다.
return new AlertDialog.Builder(Camera.<wbr />this)
.setTitle("사진 첨부하기")
.setItems(R.array.pick_image,
new DialogInterface.<wbr />OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {

/* User clicked so do some stuff */
switch (which) {
case 0:
Intent cameraIntent = new Intent(MediaStore.ACTION_<wbr />IMAGE_CAPTURE);
startActivityForResult(<wbr />cameraIntent,CAMERA);
break;
case 1:
Intent albumIntent = new Intent(
Intent.ACTION_PICK);
albumIntent
.setType(android.provider.<wbr />MediaStore.Images.Media.<wbr />CONTENT_TYPE);
startActivityForResult(<wbr />albumIntent,
ALBUM);
break;
case 2:
Intent contentIntent = new Intent(
Intent.ACTION_GET_CONTENT);
contentIntent.setType("image/*<wbr />");
startActivityForResult(Intent
.createChooser(contentIntent,
"사진 첨부하기"), CHOOSER);
break;
}
}).create();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 카메라 Intent를 startActivityForResult로 호출하였기 때문에
// 이 메서드를 재정의하면 촬영된 이미지를 처리하실 수 있습니다.
Intent intent;
File toFile = null;
File fromFile = null;
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
switch (requestCode) {
// 만약 카메라로 촬영했다면
case CAMERA:
if (resultCode == RESULT_CANCELED) return;

     if (cropedImageUri != null) {
     // 혹시 미리 선택된 이미지가 있다면 삭제합니다.
     // 이는 이미지를 복사하여 원본을 유지하고 복사본을 다루기 때문에
     // 복사본을 사용하지 않을 경우 삭제하여 공간을 낭비하지 않기 위함입니다.
File f = new File(cropedImageUri.getPath())<wbr />;
if (f.exists()) {
f.delete();
}
     }
Uri uriImages = MediaStore.Images.Media.<wbr />EXTERNAL_CONTENT_URI;
int id = -1;
String[] IMAGE_PROJECTION = {
 MediaStore.Images.<wbr />ImageColumns.DATA,
 MediaStore.Images.<wbr />ImageColumns._ID,
 MediaStore.Images.<wbr />ImageColumns.DATE_TAKEN
};
try {
Cursor cursorImages = getContentResolver().query(<wbr />uriImages, IMAGE_PROJECTION, null, null, MediaStore.Images.<wbr />ImageColumns.DATE_TAKEN + " ASC");
if (cursorImages != null && cursorImages.moveToLast()) {
fromFile = new File(cursorImages.getString(0)<wbr />);
id = cursorImages.getInt(1);
cursorImages.close();
}
} catch(Exception e) {
e.printStackTrace();
}
// 이미지 복사본을 만들기 위한 작업니다.
new File(Environment.<wbr />getExternalStorageDirectory().<wbr />getAbsolutePath() + "/myFolder").mkdirs();
toFile = new File(Environment.<wbr />getExternalStorageDirectory().<wbr />getAbsolutePath() + "/myFolder/" + System.currentTimeMillis() + ".jpg");
if (toFile.exists()) {
toFile.delete();
}
try {
// 이미지를 복사합니다.
inputStream = new FileInputStream(fromFile);
outputStream = new FileOutputStream(toFile);
FileChannel fcin =  inputStream.getChannel();
FileChannel fcout = outputStream.getChannel();

long size = fcin.size();
    
fcin.transferTo(0, sizefcout);

fcout.close();
fcin.close();
outputStream.close();
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 원본 사진 지우기
fromFile.delete();
// 원본 DB에서 지우기 
getBaseContext().<wbr />getContentResolver().delete(<wbr />MediaStore.Images.Media.<wbr />EXTERNAL_CONTENT_URI, MediaStore.Images.<wbr />ImageColumns._ID + "=" + id, null);
cropedImageUri = Uri.fromFile(toFile);
intent = new Intent("com.android.camera.<wbr />action.CROP");
intent.setDataAndType(<wbr />cropedImageUri, "image/*");
intent.putExtra("scale", true);
intent.putExtra("output", cropedImageUri);
startActivityForResult(intentCROP);
break;
case ALBUM
case CHOOSER:
// 갤러리 또는 앱에서 선택한 이미지 역시 복사본을 사용합니다.
if (resultCode == RESULT_CANCELED) return;

     if (cropedImageUri != null) {
File f = new File(cropedImageUri.getPath())<wbr />;
if (f.exists()) {
f.delete();
}
     }
cropedImageUri = data.getData();
Cursor c = getContentResolver().query(<wbr />cropedImageUri, null,null,null,null);
c.moveToNext();
String path = c.getString(c.getColumnIndex(<wbr />MediaStore.MediaColumns.DATA))<wbr />;
fromFile = new File(path);
c.close();
new File(Environment.<wbr />getExternalStorageDirectory().<wbr />getAbsolutePath() + "/myFolder").mkdirs();
toFile = new File(Environment.<wbr />getExternalStorageDirectory().<wbr />getAbsolutePath() + "/myFolder/" + System.currentTimeMillis() + ".jpg");
if (toFile.exists()) {
toFile.delete();
}
try {
inputStream = new FileInputStream(fromFile);
outputStream = new FileOutputStream(toFile);
FileChannel fcin =  inputStream.getChannel();
FileChannel fcout = outputStream.getChannel();

long size = fcin.size();
    
fcin.transferTo(0, sizefcout);

fcout.close();
fcin.close();
outputStream.close();
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
cropedImageUri = Uri.fromFile(toFile);
intent = new Intent("com.android.camera.<wbr />action.CROP");
intent.setDataAndType(<wbr />cropedImageUri, "image/*");

// intent.putExtra("outputX", GlobalData.dip(this, 460));
// intent.putExtra("outputY", GlobalData.dip(this, 345));
// intent.putExtra("aspectX", 4);
// intent.putExtra("aspectY", 3);
intent.putExtra("scale", true);
intent.putExtra("output", cropedImageUri);
startActivityForResult(intent, CROP);
break;
case CROP: 
if (resultCode == RESULT_OK)
{
cameraButton.setImageURI(null)<wbr />;
cameraButton.setImageURI(<wbr />cropedImageUri);
}
else {
     if (cropedImageUri != null) {
File f = new File(cropedImageUri.getPath())<wbr />;
if (f.exists()) {
f.delete();
}
     }
}
break;
}
}
}




댓글 없음:

댓글 쓰기