Jetpack Compose Custom CameraView 촬영-프리뷰 구현하기
현재 서비스중인 앱의 카메라 기능은 전부 XML 기반의 커스텀뷰를 만들어서 사용중이다.
Compose를 공부하는 기회에 해당 부분을 Compose로 구현해 봤다.
정말 간단한 기능의 카메라 촬영, 프리뷰 기능만을 구현한 코드를 기록해 두려한다.
코드의 가독성이나 이슈가될 만한 부분들은 상황에 맞게 조절하여 사용하시면 됩니다.
@Composable
fun CameraComponent(
modifier: Modifier = Modifier,
selfMode: Boolean,
takeAction: Boolean,
receiveImageUrl: (Bitmap?) -> Unit
) {
val cameraSelector = if (selfMode) {
CameraSelector.DEFAULT_FRONT_CAMERA
} else {
CameraSelector.DEFAULT_BACK_CAMERA
}
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val imageCapture = remember { ImageCapture.Builder().build() }
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val preview: androidx.camera.core.Preview = androidx.camera.core.Preview.Builder().build()
val cameraViewModel: CameraComponentModel = hiltViewModel()
val imageBitmap by cameraViewModel.imageBitmap.collectAsState()
var processing: Boolean by remember { mutableStateOf(false) }
// 카메라 바인딩
// cameraProvider를 통해 ProcessCameraProvider를 비동기적으로 가져와 카메라의 생명주기를 현재 카메라 컴포넌트 생명주기와 바인딩
// preview, imageCapture를 lifecycleOwner에 바인딩하여 카메라 미리보기와 이미지 캡쳐를 구현
// 카메라 세션 준비가 완료되면 프리뷰를 화면에 표시
LaunchedEffect(cameraProviderFuture, cameraSelector) {
cameraProviderFuture.addListener({
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageCapture
)
} catch (exc: Exception) {
Timber.e("Camera binding failed")
}
}, ContextCompat.getMainExecutor(context))
}
// takeAction 상태가 true로 변경될 때, imageCapture의 takePicture 메서드를 호출하여 이미지 캡쳐
// 촬영 성공 시, onCaptureSuccess 콜백에서 이미지를 비트맵으로 변환하고, 사용자 지정 작업 수행
// 실패 시, onError 콜백에서 실패 이유를 로그로 출력하고, 사용자 지정 작업 수행
LaunchedEffect(takeAction, imageBitmap) {
if (takeAction && !processing) {
imageCapture.takePicture(
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
processing = true
cameraViewModel.processImageCapture(image, selfMode)
}
override fun onError(exception: ImageCaptureException) {
Timber.e("Image capture failed ${exception.message}")
receiveImageUrl(null)
}
}
)
}
if (imageBitmap != null) {
receiveImageUrl(imageBitmap)
cameraViewModel.clearImageBitmap()
processing = false
}
}
AndroidView(
modifier = modifier.clipToBounds(),
factory = { ctx ->
PreviewView(ctx).apply {
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
}
},
update = { previewView ->
preview.setSurfaceProvider(previewView.surfaceProvider)
}
)
}
카메라 비율을 일정하게 고정시키고 싶으면 clipToBounds() 부분을 지우면 된다.
Image를 프레임에 맞춰 자르기 위해 modifier.clipToBounds() 를 통해 조절해 봤다.
댓글
댓글 쓰기