최초작성 : 17.04.13
최종수정 : 17.12.17
- rotate함수 추가 (17.08.16)
- 권한 요청 포스팅 링크 추가 (17.12.17)
- 카메라로 사진 가져올시 7.0 대응 (17.12.17)
- 코틀린으로 코드 변경, 코드보완 - 고용량 이미지 및 디바이스 파편화 (20.03.29)
앱을 만들다 보면 사진을 사용하여 이미지뷰에 뿌려주거나 업로드하는 경우가 종종 있습니다. 카메라나 기기 갤러리의 접근하기 위해서는 권한이 필요합니다. 권한 포스팅을 참고해주세요.
[안드로이드/Android] 권한 체크하기
가져온 이미지 리사이즈는 아래 포스팅을 참고해주세요
[안드로이드/Android] 이미지 리사이즈
권한 설정
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
카메라로 찍은 사진을 가져오는 경우
인텐트를 통해 카메라를 호출합니다. 'CAMERA_CODE'는 requestCode 선택한 사진에 대한 요청 값을 구분하는 용도입니다.
|
private final int CAMERA_CODE = 1111;
private void selectPhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, CAMERA_CODE);
}
-7.0 이슈 대응
7.0 이전 버전은 상위의 코드처럼 간단하게 수행이 가능했지만 보안상의 이슈로 'FileProvider.getUriForFile()'을 통해 사진의 uri를 가져오는데 이를 수행하기 위해선 'AndroidMenifest.xml'에 명시해야합니다. 7.0 버전에 대한 상세한 이슈는 추후 포스팅으로 다루겠습니다.
|
private Uri photoUri;
private String currentPhotoPath;//실제 사진 파일 경로
String mImageCaptureName;//이미지 이름
private void selectPhoto() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
}
if (photoFile != null) {
photoUri = FileProvider.getUriForFile(this, getPackageName(), photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, CAMERA_CODE);
}
}
}
}
-AndroidManifest.xml 파일 수정
authorities에는 프로젝트의 패키지명을 작성함으로써 상단 'FileProvider.getUriForFile()'가 정상작동합니다. 그리고 res폴더 하위에 xml폴더를 생성하고 'file_path' 이름의 xml파일을 생성해야합니다.
|
<application
.....>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="패키지명"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
<activity
...>
-file_path.xml 파일
파일의 경로를 명시해주는 파일로 path에 해당하는 값은 추후 'Enviroment.getExternalStorageDirectory()' 다음에 사용될 경로 이름이므로 자유롭게 사용하되 카메라 사진을 가져올때 경로 이름과 일치해야한다.
|
<paths xmlns:android = "http://schemas.android.com/apk/res/android">
<external-path name="hidden" path="path"/>
</paths>
-파일 생성
카메라로 찍은 사진을 실제 파일로 생성하는 코드입니다. 상단에 지정한 path이름과 일치해야 에러없이 해당 기능을 수행합니다.
|
private File createImageFile() throws IOException {
File dir = new File(Environment.getExternalStorageDirectory() + "/path/");
if (!dir.exists()) {
dir.mkdirs();
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
mImageCaptureName = timeStamp + ".png";
File storageDir = new File(Environment.getExternalStorageDirectory().getAbsoluteFile() + "/path/"
+ mImageCaptureName);
currentPhotoPath = storageDir.getAbsolutePath();
return storageDir;
}
-카메라로 찍은 사진 적용
private void getPictureForPhoto() {
Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath);
ExifInterface exif = null;
try {
exif = new ExifInterface(currentPhotoPath);
} catch (IOException e) {
e.printStackTrace();
}
int exifOrientation;
int exifDegree;
if (exif != null) {
exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
exifDegree = exifOrientationToDegrees(exifOrientation);
} else {
exifDegree = 0;
}
ivImage.setImageBitmap(rotate(bitmap, exifDegree));//이미지 뷰에 비트맵 넣기
}
갤러리에서 찍은 사진을 가져오는 경우
인텐트를 통해 갤러리를 호출합니다 . 'GALLERY_CODE'는 requestCode 선택한 사진에 대한 요청 값을 구분하는 용도입니다. setType 에 대한 내용은 해당 포스팅 마지막에 나열하겠습니다.
|
private final int GALLERY_CODE=1112;
private void selectGallery() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
startActivityForResult(intent, GALLERY_CODE);
}
-선택한 사진 데이터 처리
카메라로 사진을 찍거나 갤러리에서 사진 선택에 대한 사용자가 응답을 할경우 'onActivityResult'가 실행되는데 사진을 선택했을 경우 resultCode값은 'RESULT_OK' 가 취소 했을때는 'RESULT_CANCEL' 입니다. 사진 데이터는 intent 타입으로 반환됩니다.
|
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case GALLERY_CODE:
sendPicture(data.getData()); //갤러리에서 가져오기
break;
case CAMERA_CODE:
getPictureForPhoto(); //카메라에서 가져오기
break;
default:
break;
}
}
}
private void sendPicture(Uri imgUri) {
String imagePath = getRealPathFromURI(imgUri); // path 경로
ExifInterface exif = null;
try {
exif = new ExifInterface(imagePath);
} catch (IOException e) {
e.printStackTrace();
}
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int exifDegree = exifOrientationToDegrees(exifOrientation);
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);//경로를 통해 비트맵으로 전환
ivImage.setImageBitmap(rotate(bitmap, exifDegree));//이미지 뷰에 비트맵 넣기
}
-사진의 회전값 가져오기
사진의 회전값을 처리하지 않으면 사진을 찍은 방향대로 이미지뷰에 처리되지 않습니다.
|
private int exifOrientationToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}
-사진을 정방향대로 회전하기
private Bitmap rotate(Bitmap src, float degree) {
// Matrix 객체 생성
Matrix matrix = new Matrix();
// 회전 각도 셋팅
matrix.postRotate(degree);
// 이미지와 Matrix 를 셋팅해서 Bitmap 객체 생성
return Bitmap.createBitmap(src, 0, 0, src.getWidth(),
src.getHeight(), matrix, true);
}
-사진의 절대경로 구하기
사진이 저장된 절대 경로를 가져옵니다. |
private String getRealPathFromURI(Uri contentUri) {
int column_index=0;
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
if(cursor.moveToFirst()){
column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
}
return cursor.getString(column_index);
}
이상 갤러리와 카메라에서 이미지를 가져오는 방법이였습니다.
그 외에 사진 및 파일을 탐색할때 인텐트의 타입의 따라 보여지는 파일들과 뷰가 달라질 수 있습니다.
-모든 type의 파일들을 호출 하려면
- intent.setType("*/*"); |
-이미지 파일을 호출 하려면
- intent.setType("image/*"); |
-비디오 파일을 호출 하려면
- intent.setType("video/*"); |
위에 나온것 외에도 자신이 원하는 파일의 경로나 타입을 통해 sort해서 보여줄 수 있습니다.
※필자 본인을 위한 포스팅으로써 다소 미흡할 수 있습니다. 그래도 궁금하신점이 있으시다면 최대한 도움을 드리도록 하겠습니다.※
'안드로이드 > 자바' 카테고리의 다른 글
[안드로이드/Android] 안드로이드 스튜디오 2.3에서 앱 릴리즈시 이슈사항 (0) | 2017.05.15 |
---|---|
[안드로이드/Android] 안드로이드 스튜디오 설치 (0) | 2017.04.20 |
[안드로이드/Android] EditText사용시 키보드가 UI를 가릴때 (9) | 2017.04.13 |
[안드로이드/Android] Dialog 와 AlertDialog 사용하기 (0) | 2017.04.12 |
[안드로이드/Android] Firebase auth 구글 로그인 연동 (0) | 2017.04.11 |