5.4 KiB
5.4 KiB
Remove Camera1 implementation
- Delete all
android.hardware.Camerainstances. - Delete
SurfaceViewandSurfaceHolder.CallbackimplementationssurfaceCreated,surfaceChanged, andsurfaceDestroyed. - Remove custom lifecycle handling that opens or releases the camera in
onResumeoronPause. - Remove manual matrix calculations for orientation.
Initialize ProcessCameraProvider
Request the ProcessCameraProvider and bind use cases to the Activity or
Fragment lifecycle.
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(context, lifecycleOwner) {
val cameraProvider = ProcessCameraProvider.getInstance(context).await()
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
cameraProvider.unbindAll() // Unbind before rebinding
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageCapture
)
val cameraControl = camera.cameraControl
}
Implement the preview and tap-to-focus
Choose exactly one of the following patterns based on the app's UI toolkit:
Option A: For Android Views
Use androidx.camera.view.PreviewView.
-
Set up preview:
preview.setSurfaceProvider(previewView.surfaceProvider)
-
Handle tap-to-focus:
val factory = previewView.meteringPointFactory val point = factory.createPoint(x, y) // x, y from touch event val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build() cameraControl?.startFocusAndMetering(action)
Option B: For Jetpack Compose
Use androidx.camera.compose.CameraXViewfinder.
-
Set up preview and SurfaceRequest:
var surfaceRequest by remember { mutableStateOf<SurfaceRequest?>(null) } val preview = remember { Preview.Builder().build().apply { setSurfaceProvider { request -> surfaceRequest = request } } }
-
Render viewfinder:
surfaceRequest?.let { request -> CameraXViewfinder( surfaceRequest = request, coordinateTransformer = coordinateTransformer, modifier = Modifier ) }
-
Handle tap-to-focus in Compose:
// Inside your tap gesture handler... val surfaceCoords = with(coordinateTransformer) { offset.transform() } val factory = SurfaceOrientedMeteringPointFactory( request.resolution.width.toFloat(), request.resolution.height.toFloat() ) val point = factory.createPoint(surfaceCoords.x, surfaceCoords.y) val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build() cameraControl?.startFocusAndMetering(action)
-
Update target rotation for Compose:
LaunchedEffect(configuration) { if (!view.isInEditMode) { val rotation = view.display?.rotation ?: Surface.ROTATION_0 imageCapture.targetRotation = rotation preview.targetRotation = rotation } }
Capture a photo
Use the ImageCapture use case to take the picture. The ImageProxy handles
rotation directly.
imageCapture.takePicture(
cameraExecutor,
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
// Adjust rotation natively via ImageProxy
val matrix = Matrix()
matrix.postRotate(image.imageInfo.rotationDegrees.toFloat())
if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
matrix.postScale(-1f, 1f) // Mirror for front camera
}
val rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
// MUST close proxy
image.close()
}
override fun onError(exception: ImageCaptureException) {
Log.e("CameraX", "Capture failed: ${exception.message}", exception)
}
}
)
Switch cameras
To flip between front and rear cameras, change the CameraSelector and
retrigger the ProcessCameraProvider logic.
lensFacing = if (lensFacing == CameraSelector.LENS_FACING_BACK) {
CameraSelector.LENS_FACING_FRONT
} else {
CameraSelector.LENS_FACING_BACK
}
Follow constraints
- Don't manage the camera lifecycle manually : Bind the camera to a
LifecycleOwnerthrough theProcessCameraProvider. Avoid manual camera open or close logic inonResumeoronPause. - Don't calculate focus matrices manually :
MeteringPointFactoryhandles coordinate transformations, including device rotation offsets. Avoid custom matrix implementations. - Don't forget to close the
ImageProxy: Remember to invokeimage.close()in the capture callback. Skipping this call locks the capture pipeline and interrupts subsequent photos. - Don't wrap
PreviewViewinAndroidViewfor Compose code : For Compose UI layouts, useCameraXViewfinder. CompilingPreviewViewin anAndroidViewis an earlier fallback option that introduces resizing issues.