[Android] ActivityResultContracts 사용해보기
평소에 이미지를 촬영하거나 사진을 선택할 때 외부 라이브러리를 가져다 쓰는데
간단한 기능을 사용하는데도 필요한가 싶어서 찾아봤는데 당연하게도 있었다.
AndroidX에 ActivityResultsAPI 에 추가된 기능이다.
보통 다른 Activity에서 결과를 받을 때 startActivityForResult()
와 onActivityResult()
를 이용한다.
AndroidResultAPI를 이용하여 동일한 방법으로 처리하고, 다양한 기능을 이용할 수 있다. 이제 외부 라이브러리를 가져다 쓰지 않아도 된다는 말이다.
ActivityResultContracts
ActivityResultContracts 에서 제공하는 기능은 다양한데. 사진 촬영부터 시작해서 문서 생성, 폴더 접근 등 다양하게 지원한다. 직접 Activity를 호출할 경우도 ActivityResultContract<I,O>
추상 클래스를 이용하여 직접 구현할 수 있다.
이번에는 간단하게 어떤식으로 호출이되고, 데이터를 받는지 확인만 해보도록 하려고 한다.
아래 두가지를 이용해서 테스트를 진행했다.
ActivityResultContracts.TakePicture()
ActivityResultContracts.GetContent()
TakePicture()
를 이용하여 사진을 찍고, GetContent()
를 이용하여 사진을 가져온다.
프로젝트
Github Sample Project
개발
사용하기 위해서 registerForActivityResult
메서드를 통해 다음과 같이 액티비티 결과 콜백을 등록한다.
private lateinit var takePicture: ActivityResultLauncher<Uri>
// ---
takePicture =
registerForActivityResult(
ActivityResultContracts.TakePicture()) { isTakePicture ->
if (isTakePicture) {
Toast.makeText(this, "사진을 찍었습니다", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "사진을 찍어주세요", Toast.LENGTH_SHORT).show()
}
}
registerForActivityResult
로 등록 후 얻은 ActivityResultLauncher
인스턴스로 액티비티를 실행할 수 있다.
ActivityResultLauncher
의 launch
메서드를 호출하여 결과를 받을 Activity를 생성한다.
TakePicture()
를 이용하기 위해서는 이미지가 저장되기 위한 Uri가 필요하다. 임시 파일생성 후 FileProvider
를 이용하여 이미지가 저장될 Uri를 launch
에 파라미터로 넘겨준다.
val imageFile = createFileInFiles(applicationContext, "/images")
currentImageUri = FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", imageFile)
if (currentImageUri != null)
takePicture.launch(currentImageUri)
private fun loadBitmapFromUri(uri: Uri) = runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.decodeBitmap(
ImageDecoder.createSource(
contentResolver,
uri
)
)
} else {
MediaStore.Images.Media.getBitmap(contentResolver, uri)
}
}.getOrElse {
val `is` = applicationContext.contentResolver.openInputStream(uri)
BitmapFactory.decodeStream(`is`)
}
private fun createFileInFiles(context: Context, path: String, prefix: String? = null): File {
val timeStamp: String = System.currentTimeMillis().toString()
val fileName =
"${path}/${if (prefix != null && prefix.isBlank()) prefix else ""}_${timeStamp}.jpeg"
val file = File(getAppFilesDir(context), fileName)
if (file.parentFile?.exists() == false) {
file.parentFile?.mkdirs()
}
if (!file.exists()) {
file.createNewFile()
}
return file
}
private fun getAppFilesDir(context: Context): File? {
val file = context.filesDir
if (file != null && !file.exists()) {
file.mkdirs()
}
return file
}