How Can I Use A Cameraview With Jetpack Compose?
Solution 1:
At the moment there isn't any official Composable function for CameraX so we have to inflate the legacy android view inside compose.
To achieve that
we can use AndroidView composable function,
it accepts two parameters
- @param 
resIdThe id of the layout resource to be inflated. - @param 
postInflationCallbackThe callback to be invoked after the layout is inflated. 
and to access the lifecycle and context we use the ambients
vallifecycleOwner= LifecycleOwnerAmbient.current
valcontext= ContextAmbient.current
As we have everything we need let's do it:
Create a layout camera_host.xml
<?xml version="1.0" encoding="utf-8"?><androidx.camera.view.PreviewViewxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/previewView"android:layout_width="match_parent"android:layout_height="match_parent" />and inflate it using AndroidView Composable function.
@ComposablefunSimpleCameraPreview() {
    val lifecycleOwner = LifecycleOwnerAmbient.current
    val context = ContextAmbient.current
    val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
    AndroidView(resId = R.layout.camera_host) { inflatedLayout ->
       //You can call// findViewById<>() and etc ... on inflatedLayout// here PreviewView is the root of my layout so I just cast it to// the PreviewView and no findViewById is required
        cameraProviderFuture.addListener(Runnable {
            val cameraProvider = cameraProviderFuture.get()
            bindPreview(
                 lifecycleOwner,
                 inflatedLayout as PreviewView /*the inflated layout*/,
                 cameraProvider)
        }, ContextCompat.getMainExecutor(context))
    }
}
funbindPreview(
    lifecycleOwner: LifecycleOwner,
    previewView: PreviewView,
    cameraProvider: ProcessCameraProvider
) {
    var preview: Preview = Preview.Builder().build()
    var cameraSelector: CameraSelector = CameraSelector.Builder()
        .requireLensFacing(CameraSelector.LENS_FACING_BACK)
        .build()
    preview.setSurfaceProvider(previewView.createSurfaceProvider())
    var camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
}
classMainActivity : AppCompatActivity() {
    overridefunonCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SimpleCameraPreview()
        }
    }
}
Solution 2:
There is still no CameraX composable. You need to use AndroidView to create one.
Updated example for Compose 1.0.0-beta02:
@ComposablefunCameraPreview(
    modifier: Modifier = Modifier,
    cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA,
    scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER,
) {
    val lifecycleOwner = LocalLifecycleOwner.current
    AndroidView(
        modifier = modifier,
        factory = { context ->
            val previewView = PreviewView(context).apply {
                this.scaleType = scaleType
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
                // Preview is incorrectly scaled in Compose on some devices without this
                implementationMode = PreviewView.ImplementationMode.COMPATIBLE
            }
            val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
            cameraProviderFuture.addListener({
                val cameraProvider = cameraProviderFuture.get()
                // Previewval preview = Preview.Builder()
                    .build()
                    .also {
                        it.setSurfaceProvider(previewView.surfaceProvider)
                    }
                try {
                    // Must unbind the use-cases before rebinding them.
                    cameraProvider.unbindAll()
                    cameraProvider.bindToLifecycle(
                        lifecycleOwner, cameraSelector, preview
                    )
                } catch (exc: Exception) {
                    Log.e(TAG, "Use case binding failed", exc)
                }
            }, ContextCompat.getMainExecutor(context))
            previewView
        })
}
Solution 3:
I've created a library to use CameraX in Jetpack Compose. It might be useful until an official library comes out.
https://github.com/skgmn/CameraXX
In your build.gradle, (requires GitHub personal access token)
implementation "com.github.skgmn:cameraxx-composable:0.3.0"Composable method signature
CameraPreview(
    modifier: Modifier = Modifier,
    cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA,
    preview: Preview?,
    imageCapture: ImageCapture? = null,
    imageAnalysis: ImageAnalysis? = null
)
You can omit preview parameter to use default Preview instance.
An example
classMainViewModel : ViewModel() {
    val imageCapture = ImageCapture.Builder().build()
}
@ComposablefunMain() {
    val viewModel: MainViewModel = viewModel()
    val imageCapture by remember { viewModel.imageCapture }
    CameraPreview(Modifier.fillMaxSize(), imageCapture)
}
Solution 4:
This is my snippet (based on Sean's answer), which also handles torch state and resource disposition and adds a focus on tap logic. Dependencies:
implementation 'androidx.camera:camera-camera2:1.1.0-alpha11'
implementation 'androidx.camera:camera-view:1.0.0-alpha31'
implementation 'androidx.camera:camera-lifecycle:1.1.0-alpha11'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0-RC'@ComposablefunCameraPreview(
    modifier: Modifier = Modifier,
    cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA,
    implementationMode: PreviewView.ImplementationMode = PreviewView.ImplementationMode.COMPATIBLE,
    scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER,
    imageAnalysis: ImageAnalysis? = null,
    imageCapture: ImageCapture? = null,
    preview: Preview = remember { Preview.Builder().build() },
    enableTorch: Boolean = false,
    focusOnTap: Boolean = false
) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val cameraProvider by produceState<ProcessCameraProvider?>(initialValue = null) {
        value = ProcessCameraProvider.getInstance(context).await()
    }
    // TODO: add cameraSelectorval camera = remember(cameraProvider) {
        cameraProvider?.let {
            it.unbindAll()
            it.bindToLifecycle(
                lifecycleOwner,
                cameraSelector,
                *listOfNotNull(imageAnalysis, imageCapture, preview).toTypedArray()
            )
        }
    }
    LaunchedEffect(camera, enableTorch) {
        camera?.let {
            if (it.cameraInfo.hasFlashUnit()) {
                it.cameraControl.enableTorch(enableTorch).await()
            }
        }
    }
    DisposableEffect(Unit) {
        onDispose {
            cameraProvider?.unbindAll()
        }
    }
    AndroidView(
        modifier = modifier.pointerInput(camera, focusOnTap) {
            if (!focusOnTap) return@pointerInput
            detectTapGestures {
                val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
                    size.width.toFloat(),
                    size.height.toFloat()
                )
                val meteringAction = FocusMeteringAction.Builder(
                    meteringPointFactory.createPoint(it.x, it.y),
                    FocusMeteringAction.FLAG_AF
                ).disableAutoCancel().build()
                camera?.cameraControl?.startFocusAndMetering(meteringAction)
            }
        },
        factory = { _ ->
            PreviewView(context).also {
                it.scaleType = scaleType
                it.implementationMode = implementationMode
                preview.setSurfaceProvider(it.surfaceProvider)
            }
        }
    )
}
Post a Comment for "How Can I Use A Cameraview With Jetpack Compose?"