앱에서 카메라로 찍은 사진을 갤러리에 저장까지 해보자.
ContentResolver, MediaStore 를 사용하는 방식이다.
✅ Manifest 파일에 권한 추가
카메라와 갤러리에 접근해야 하기 때문에 권한을 추가해주자.
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
✅ Permission 요청 세팅
이전 포스팅들과 마찬가지로,
TedPermission 라이브러리를 활용하여 간편하게 권한을 요청해준다.
private fun checkPermission() {
TedPermission.create()
.setPermissionListener(object : PermissionListener {
override fun onPermissionGranted() {
openCamera()
}
override fun onPermissionDenied(deniedPermissions: List<String>) {
Log.d(TAG, "onPermissionDenied: 권한 거부됨")
}
})
.setDeniedMessage("권한을 허용해 주셔야\n사용 가능합니다.")
.setPermissions(android.Manifest.permission.READ_MEDIA_IMAGES, android.Manifest.permission.CAMERA)
.check()
}
openCamera() 함수에 카메라로 전환하는 코드를 작성할 것이다.
✅ 카메라 전환
ActivityResultLauncher를 사용하여 기기의 기본 카메라로 전환한다.
갤러리에서 이미지를 접근하는 예제도 Intent를 사용했지만, Intent action이 다르다.
(갤러리 접근 : Intent.ACTION_PICK / 카메라 전환 : MediaStore.ACTION_IMAGE_CAPTURE)
private var uri: Uri? = null
private fun openCamera() {
val fileName = "${System.currentTimeMillis()}.jpg"
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/Camera")
}
uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)!!
Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply {
putExtra(MediaStore.EXTRA_OUTPUT, uri)
launcher.launch(this)
}
}
카메라로 전환하기 전, ContentResolver와 MediaStore를 활용하여
갤러리에 저장될 파일을 미리 생성해준다.
(이 때, 데이터가 비어있는 파일이 생성된다)
✅ 사진 촬영 후 콜백 처리
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
Toast.makeText(this, "이미지 저장 완료", Toast.LENGTH_SHORT).show()
}
카메라 Activity에서 되돌아오면, ActivityResult 콜백을 받게 된다.
간단하게 토스트를 띄우고 마무리한 뒤, 갤러리에 저장이 되었는지 확인까지 해보자.
✅ 결과
● 기본 UI
위 버튼을 누르면 openCamera() 함수가 실행된다.
● 카메라 전환 후 사진 촬영 완료
● 갤러리
갤러리에 이미지가 저장된 것을 확인할 수 있다.
✅ 수정 사항
기존에는 Bitmap 데이터를 저장하는 방식을 많이 사용하곤 했지만,
현재 시점에서는 MediaStore와 ContentResolver를 사용하는 방식을 더 권장한다.
하지만 위 방식에도 치명적인 단점이 존재하는데,
임시 파일을 갤러리에 '미리' 생성한다는 점이다.
카메라 Activity로 이동하는 시점에 임시 파일이 생성되는데,
이 때 카메라를 취소하고 되돌아오면 갤러리에는 위 처럼 비어있는 파일이 생겨버린다.
해당 이미지 파일의 상세 정보를 보면,
경로와 파일명은 코드에서 지정해준대로 명시되어 있는 것을 확인할 수 있다.
위 문제를 해결하고자 다음과 같이 resultCode에 따라 분기 처리를 해준다.
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
Toast.makeText(this, "이미지 저장 완료", Toast.LENGTH_SHORT).show()
} else {
contentResolver.delete(uri!!, null)
}
}
resultCode가 OK가 아닐 때, 해당 uri 경로의 파일을 삭제하는 방법이다.
이제 카메라를 취소하면 임시 파일은 자동으로 삭제된다.
✅ 이미지뷰에 로드
Uri 객체에 해당 이미지 파일의 경로를 초기화했기 때문에,
ActivityResult 콜백 이후에 간단히 로드할 수 있다.
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
Toast.makeText(this, "이미지 저장 완료", Toast.LENGTH_SHORT).show()
bind.imageview.setImageURI(uri)
} else {
contentResolver.delete(uri!!, null)
}
}
이미지뷰에 해당 이미지가 로드된 모습이다.
이미지를 외부 앱에 공유하기,
갤러리에서 이미지 불러오기,
스크린 샷 찍고 갤러리에 저장하기,
카메라로 사진 촬영 후 저장하기.
총 4차례의 포스팅에 걸쳐서 Bitmap, MediaStore, FileProvider 등
다양한 방법으로 기기 저장소와 이미지를 다뤄보았다.
이전에 한 번씩은 사용해보았던 방법들인데, 모두 가능한 방법들이다보니
막상 다시 사용하려고 하면 머릿속에 섞이고 헷갈려서 정리 겸 포스팅했다.
'Android (안드로이드)' 카테고리의 다른 글
[Android] ExoPlayer - RTSP 스트리밍 연결 (0) | 2024.11.28 |
---|---|
[Android] RTSP - 실시간 스트리밍 프로토콜 (0) | 2024.11.27 |
[Android] 화면 스크린샷 찍고 갤러리에 저장하기 (0) | 2024.11.25 |
[Android] 갤러리에서 이미지 불러오기 (15) | 2024.11.13 |
[Android] 다른 앱에 데이터 공유하기 / 외부 앱에 이미지 공유하기 (1) | 2024.10.10 |