일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 |
- 목적중심리더십
- 북한살둘레길
- 지지않는다는말
- 리얼하다
- 브런치작가되기
- 안드로이드
- 소프시스 밤부 좌식 엑슬 테이블
- 끝말잇기
- 소프시스
- 캐치마인드
- 베드트레이
- 한달어스
- 좌식테이블
- T자형인재
- 인터뷰
- 어떻게 나답게 살 것인가
- 베드테이블
- 목적 중심 리더십
- 테트리스
- 면접
- 아비투스
- 함수형 프로그래밍
- 재택근무
- 한달브런치북만들기
- 한단어의힘
- 한달독서
- 슬기로운 온라인 게임
- 자취필수템
- 프래그먼트
- 1일1커밋
- Today
- 139
- Total
- 874,662
정상에서 IT를 외치다
[Android,GetImage] 카메라와 갤러리에서 이미지 가져오기 본문
안녕하세요. 블랙진입니다!!
이번에는 어마무시한 포스팅을 진행해 볼려고 합니다. 바로 '카메라와 갤러리에서 이미지 가져오기' 입니다. 단계 별로 포스팅을 진행하여 여러분 께서 쉽게 따라하고 이해할 수 있게 준비해 보았습니다. 포스팅 순서는 아래와 같이 진행하겠습니다.
1. 카메라와 갤러리에서 이미지 가져오기
2. Nougat(Android OS 7) 대응하기
3. 카메라회전 각도를 고려한 이미지 가져오기
4. 가져온 이미지 크롭(Crop) 하기
Chapter 1. 카메라와 갤러리에서 이미지 가져오기
만들어볼 모든 예제는 위와 같은 이미지 입니다. Gallery 와 Camera 버튼을 통해 이미지를 받아오고 이를 화면 중앙에 보이는 예제입니다.
0. Package
GetImageActivity 와 activity_get_image 로 1개의 Activity 로 이루어져 있습니다.
activiy_get_image.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/llBottomNav"/>
<LinearLayout
android:id="@+id/llBottomNav"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="#96000000"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<Button
android:id="@+id/btnGallery"
android:text="Gallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnCamera"
android:text="Camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
1. 카메라와 갤러리에 접근 할 수 있는 권한을 받아옵니다.
AnroidManifest.xml
<!-- camera, gallery -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
AnroidManifest 에 위 3가지 권한을 추가해 줍니다.
app/build.gradle
안드로이드 마시멜로우 버전 이상부터는 권한에 대한 허용을 사용자로부터 받아야 합니다. 이에 관해서는 Tedpermission 라이브러리를 사용했습니다.
//ted permission
implementation "gun0912.ted:tedpermission:2.1.0"
app 단계의 build.gradle 의 dependencies 에 tedpermission 을 추가해 줍니다.
GetImageActivity.class
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_image);
tedPermission();
}
onCreate 에서 권한 요청을 해줍니다.
private void tedPermission() {
PermissionListener permissionListener = new PermissionListener() {
@Override
public void onPermissionGranted() {
// 권한 요청 성공
}
@Override
public void onPermissionDenied(ArrayList<String> deniedPermissions) {
// 권한 요청 실패
}
};
TedPermission.with(this)
.setPermissionListener(permissionListener)
.setRationaleMessage(getResources().getString(R.string.permission_2))
.setDeniedMessage(getResources().getString(R.string.permission_1))
.setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
.check();
}
TedPermission 이 동작되었을 때 나오는 안내 문구를 설정할 수가 있는데 저는 아래와 같은 안내 문구를 사용했습니다.
values/strings.xml
<resources>
<string name="app_name">MyImageSample</string>
<!-- Ted Permission -->
<string name="permission_1">[설정] > [권한] 에서 권한을 허용할 수 있습니다.</string>
<string name="permission_2">사진 및 파일을 저장하기 위하여 접근 권한이 필요합니다.</string>
</resources>
2. 앨범에서 이미지 가져오기
이제 권한을 설정해 주었으니 앨범에서 이미지를 가져오겠습니다.
private void goToAlbum() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(MediaStore.Images.Media.CONTENT_TYPE);
startActivityForResult(intent, PICK_FROM_ALBUM);
}
Intent 를 통해 앨범화면으로 쉽게 이동할 수 있습니다. 이 때 startActivityForResult 에 PICK_FROM_ALBUM 변수를 넣어주었습니다.
private static final int PICK_FROM_ALBUM = 1;
이 변수는 onActivityResult 에서 requestCode 로 반환되는 값입니다.
startActivityForResult 를 통해 다른 Activity 로 이동한 후 다시 돌아오게 되면 onActivityResult 가 동작되게 됩니다. 이때 startActivityForResult 의 두번 째 파라미터로 보낸 값 {여기서는 PICK_FROM_ALBUM 이겠죠?}이 requestCode 로 반환되는 동작을 합니다.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_FROM_ALBUM) {
Uri photoUri = data.getData();
Cursor cursor = null;
try {
/*
* Uri 스키마를
* content:/// 에서 file:/// 로 변경한다.
*/
String[] proj = { MediaStore.Images.Media.DATA };
assert photoUri != null;
cursor = getContentResolver().query(photoUri, proj, null, null, null);
assert cursor != null;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
tempFile = new File(cursor.getString(column_index));
} finally {
if (cursor != null) {
cursor.close();
}
}
setImage();
}
}
onActivityResult 의 requestCode 값이 PICK_FROM_ALBUM 이면 해당 로직이 실행됩니다.
data.getData() 를 통해 갤러리에서 선택한 이미지의 Uri 를 받아 옵니다. 이를 cursor 를 통해 스키마를 content:// 에서 file:// 로 변경해 줍니다. 이는 사진이 저장된 절대경로를 받아오는 과정입니다.
전역변수로 File 타입의 tempFile 을 선언해 주세요. 이 tempFile 에 받아온 이미지를 저장할거에요.
private File tempFile;
3. 갤러리에서 받아온 이미지 넣기
private void setImage() {
ImageView imageView = findViewById(R.id.imageView);
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap originalBm = BitmapFactory.decodeFile(tempFile.getAbsolutePath(), options);
imageView.setImageBitmap(originalBm);
}
전역 변수 tempFile 의 경로를 불러와 bitmp 파일로 변형한 후 imageView 에 해당 이미지를 넣어줍니다. 짜잔! 여기까지가 앨범에서 이미지를 가져와 ImageView 에 넣는 과정이었습니다 .
4. 카메라에서 이미지 가져오기
private void takePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
tempFile = createImageFile();
} catch (IOException e) {
Toast.makeText(this, "이미지 처리 오류! 다시 시도해주세요.", Toast.LENGTH_SHORT).show();
finish();
e.printStackTrace();
}
if (tempFile != null) {
Uri photoUri = Uri.fromFile(tempFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, PICK_FROM_CAMERA);
}
}
Intent 를 통해 카메라화면으로 이동할 수 있습니다. 이때 startAcitivtyResult 에는 PICK_FROM_CAMER 를 파라미터로 넣어줍니다.
private static final int PICK_FROM_CAMERA = 2;
근데 여기서 tempFile 의 Uri 경로를 intent 에 추가해 줘야 합니다. 이는 카메라에서 찍은 사진이 저장될 주소를 의미합니다. 예제에서는 tempFile 을 전역변수로 해서 사용하기 때문에 이 tempFile 에 카메라에서 촬연한 이미지를 넣어줄꺼에요!
Uri photoUri = Uri.fromFile(tempFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
5. 카메라에서 찍은 사진을 저장할 파일 만들기
private File createImageFile() throws IOException {
// 이미지 파일 이름 ( blackJin_{시간}_ )
String timeStamp = new SimpleDateFormat("HHmmss").format(new Date());
String imageFileName = "blackJin_" + timeStamp + "_";
// 이미지가 저장될 폴더 이름 ( blackJin )
File storageDir = new File(Environment.getExternalStorageDirectory() + "/blackJin/");
if (!storageDir.exists()) storageDir.mkdirs();
// 빈 파일 생성
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
return image;
}
주석에 적어 놓은 것과 같이 파일 명과 폴더명을 직접 정할 수 있습니다. 그럼 카메라에서 찍은 사진을 우리가 생성한 파일에 저장하고 받아올 수 있게 됩니다.
6. onActivityResult 분기 처리
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_FROM_ALBUM) {
Uri photoUri = data.getData();
...
setImage();
} else if (requestCode == PICK_FROM_CAMERA) {
setImage();
}
}
onActivityResult 에서 requestCode 를 앨범에서 온 경우와 카메라에서 온 경우로 나눠서 처리해줍니다.
7. 예외 사항 처리하기
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {Toast.makeText(this, "취소 되었습니다.", Toast.LENGTH_SHORT).show();
if(tempFile != null) {
if (tempFile.exists()) {
if (tempFile.delete()) {
Log.e(TAG, tempFile.getAbsolutePath() + " 삭제 성공");
tempFile = null;
}
}
}
return;
}
if (requestCode == PICK_FROM_ALBUM) {
Uri photoUri = data.getData();
...
setImage();
} else if (requestCode == PICK_FROM_CAMERA) {
setImage();
}
}
예외 사항이란 앨범화면으로 이동 했지만 선택을 하지 않고 뒤로 간 경우 또는 카메라로 촬영한 후 저장하지 않고 뒤로 가기를 간 경우입니다. 이는 resultCode 값을 통해 확인할 수 있습니다. 이 경우 "취소 되었습니다.' 토스트를 보여줍니다.
우리가 카메라 촬영할 때 createImageFile() 을 통해 temFile 을 생성했었지요? 만약 사진 촬영 중 취소를 하게 되면 temFile 이 빈썸네일로 디바이스에 저장됩니다. 따라서 예외 사항에서 tempFIle 이 존재하면 이를 삭제해 주는 작업이 필요합니다.
여기까지가 안드로이드 Nougat(OS 7) 미만 버전에서 사용했던 방식입니다.
<주의>
위 예제 코드 카메라에서 이미지 가져오기를 Nougat(OS 7, api 24) 이상 버전에서 실행하면 android.os.FileUriExposedException 에러를 발생하게 됩니다. 원활한 테스트를 위해서는 Nougat 하위 버전에서 카메라에서 이미지 가져오기를 실행해 주시거나 Nougat OS에 대응한 이미지 가져오기 포스팅을 진행해주세요.
다음 시간에는 Nougat OS에 대응한 이미지 가져오기 포스팅을 진행하겠습니다.
관련 포스팅
1. 카메라와 갤러리에서 이미지 가져오기
'안드로이드' 카테고리의 다른 글
[Android,GetImage] 카메라와 갤러리에서 이미지 가져오기 (카메라 회전각도 적용) (6) | 2018.10.23 |
---|---|
[Android,GetImage] 카메라와 갤러리에서 이미지 가져오기 (Nougat 대응) (6) | 2018.10.23 |
[Android,GetImage] 카메라와 갤러리에서 이미지 가져오기 (26) | 2018.10.23 |
[Android, Butterknife] 버터나이프 추상화 (0) | 2018.10.12 |
[Android, fragmentManager] 상태 유지된 fragment 보여주기 (8) | 2018.10.12 |
[Android, Test] 통신 테스트 하기 위한 주소 (0) | 2018.10.11 |
-
질문 2019.03.28 17:33 아아아감사합니다
-
Black-Jin 2019.03.28 20:54 신고 저도 긴글 봐주셔서 감사합니다 :D
-
카메라가 안되요ㅜㅜ 2019.04.05 15:09 안녕하세요 정말 좋은 글을 보고 따라하는 소프트웨어과 학생입니다.
애물레이터를 실행시키면 허가문구 나오고, allow를 해주면, 갤러리에서는 모든 동작이 잘 작동하지만
카메라를 누르는 순간 앱이 종료가 됩니다. 혹시 제가 놓친부분이 어딘지 알 수 있을까요? -
Black-Jin 2019.04.05 22:11 신고 안녕하세요. 사용하신 예물레이터의 버전이 Nougat(Android OS 7, API 24) 이상이면 android.os.FileUriExposedException 에러를 발생하며 종료됩니다. 예물레이터가 API 23 이하 버전에서 테스트 해주세요. 혹은 두번 째 포스팅인 "Nougat(Android OS 7) 대응하기" 를 따라서 작업해주시면 되겠습니다:)
포스팅에 이에 대한 안내가 부족했던것 같아 내용을 추가했습니다. 댓글 감사합니다. -
막무가내막내 2019.05.01 23:13 신고 덕분에 카메라 찍은 거 이미지 가져오는거 해결했습니다.
괜찮으시다면 출처남기고 제 블로그에 정리좀 해놔도 될까요??
문제가 되면 삭제하겠습니다... -
Black-Jin 2019.05.01 23:15 신고 괜찮습니다:)
도움이 되었다니 다행입니다. 감사합니다. -
막무가내막내 2019.05.01 23:18 신고 감사합니다~
-
초보개발자 2019.05.11 18:26 안녕하세요 덕분에 해결 잘했습니다!
출처 남기고 정리좀 해놔도 될까요? -
Black-Jin 2019.05.13 22:23 신고 괜찮습니다 :)
댓글 감사합니다 ~!! -
hs감기 2019.05.30 17:20 안녕하세요.
좋은 정보 감사합니다.
혹시 위의 해당 코드에서 이미지 저장후 갤러리를 들어갈때 이미지의 회전각도를 정상으로 하는 방법을
알고싶습니다. -
Black-Jin 2019.05.31 14:07 신고 안녕하세요. 이미지 회전각도는 디바이스 별로 달라 ExifInterface 를 사용해 코드를 구현해 주셔야 합니다. 관련 포스팅 3번 '카메라회전 각도를 고려한 이미지 가져오기' 를 참고하시면 좋을 것 같습니다:)
-
대학생 2019.06.09 12:50 감사합니다 덕분에 과제를 해결했어요 ㅜㅜㅜㅜㅜ
-
Black-Jin 2019.06.09 13:26 신고 도움이 되었다니 다행입니다! 감사합니다
-
궁금해요 2019.07.08 18:51 좋은글 감사합니다!
덕분에 이미지뷰에 갤러리에서 가져온사진을 띄울 수 있었습니다.
혹시 선택한 사진을 db에 등록하는 기능을 부여할 방법은 없을까요?
저는 게시글을 db에 저장하는 작업을 하는 중 이었는데 EditText나 스피너로 받은 값을 db에 저장하는데 성공했지만 사진을 db에 저장할 방법은 도저히 모르겠습니다.. 염치없지만 혹시 방법을 아신다면 조언을 부탁드립니다.. -
Black-Jin 2019.07.09 11:43 신고 안녕하세요. 글이 도움이 되었다고 하니 다행입니다. 갤러리라는 외부 저장소에서 가져온 이미지를 내부저장소에 저장하는법을 물어보시는 건가?? 이게 맞다면 캐시나 폴더를 만들어서 저장해주어야 합니다. 제 이전 포스팅 "내부저장소에 비트맵 저장하기" 포스팅을 확인하시면 도움이 될 것 같습니다. https://black-jin0427.tistory.com/179
-
sonic817 2019.09.17 16:30 선생님 혹시 사진이 있는 모든 폴더를 불러오는 기능도 구현할 수 있나요?
-
Black-Jin 2019.09.18 13:51 신고 안녕하세요. sonic817님
사진이 들어있는 폴더들을 검색하고 싶으신 건가요? 제 생각으로는 모든 사진들을 불러오고 난 후 사진들의 경로를 보고 폴더로 분류하는 작업을 해야 되지 않을까 싶습니다. -
질문이요 ㅜㅜ 2019.10.26 23:24 코드 다 복붙하고 돌렷는데 튕겨요 ㅜㅜ왜이려죠..
-
Black-Jin 2019.10.29 18:29 신고 안녕하세요. 깃허브에 있는 코드를 가지고 시도해 보세요! 아니면 튕겼을 때 에러 로그를 올려주시면 확인해보겠습니다.
-
피치 2020.02.22 00:44 안녕하세요. 혹시 xml에서 GALLERY와 CAMERA버튼을 자바 코드와 연결시키는 부분은 어디에 있나요..?
-
Black-Jin 2020.02.23 10:18 신고 안녕하세요. 피치님
버튼을 연결하는 부분은 위 포스팅에는 없고 깃허브 예제 파일을 확인해 주세요. 참고로 깃허브 파일에서 이런식으로 구현되어 있습니다.
findViewById(R.id.btnGallery).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 권한 허용에 동의하지 않았을 경우 토스트를 띄웁니다.
if(isPermission) goToAlbum();
else Toast.makeText(view.getContext(), getResources().getString(R.string.permission_2), Toast.LENGTH_LONG).show();
}
});
findViewById(R.id.btnCamera).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 권한 허용에 동의하지 않았을 경우 토스트를 띄웁니다.
if(isPermission) takePhoto();
else Toast.makeText(view.getContext(), getResources().getString(R.string.permission_2), Toast.LENGTH_LONG).show();
}
}); -
헌터 2020.05.12 11:48 안녕하세요!
카메라 촬영, 갤러리에서 이미지 가져오기까지는 정말 설명잘해주셔서 큰 도움이 됐는데
혹시 동영상 촬영, 갤러리에서 동영상 가져오기(썸네일이라도)는 어떻게 구현해야할까요?ㅠㅠ 너무어렵네요... -
thx 2020.08.24 02:30 좋은 글 정말 감사합니다! 큰 도움이 되었어요
-
Black-Jin 2020.08.31 11:55 신고 2년전 글이지만 도움이 되었다니 다행입니다. 감사합니다.
-
학생 12 2020.11.15 16:14 안녕하세요 해당 로직으로 갤러리에서 사진을 가져와서 비트맵으로 전환후 제가 원하는 이미지 뷰에 띄우주고싶은데 자꾸 이미지 뷰 자체가 죽어버리네요 이런 경우엔 어떻게 해결해야 하나요?
-
ㅇㅇ 2021.09.14 16:06 잘 보고 갑니다
덕분에 막히던 부분이 시원하게 해결됐어요!