Android (안드로이드)

[Android] 앱에서 카메라로 사진 찍고 갤러리에 저장하기 / 이미지뷰 로드하기

Oscar:) 2024. 11. 26. 23:00
728x90

 

 

 

앱에서 카메라로 찍은 사진을 갤러리에 저장까지 해보자.

 

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)
	}
}

 

카메라로 전환하기 전, ContentResolverMediaStore를 활용하여

갤러리에 저장될 파일을 미리 생성해준다.

(이 때, 데이터가 비어있는 파일이 생성된다)

 

 

 

 

 

✅ 사진 촬영 후 콜백 처리

 

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 등

다양한 방법으로 기기 저장소와 이미지를 다뤄보았다.

 

이전에 한 번씩은 사용해보았던 방법들인데, 모두 가능한 방법들이다보니

막상 다시 사용하려고 하면 머릿속에 섞이고 헷갈려서 정리 겸 포스팅했다.

 

 

 

 

 

 

 

 

728x90