Currently there's no equivalent to CameraView (and PreviewView) in Compose. Is it possible to wrap it and display it in a compose layout?
-
I haven't tried my self, but there is a sample for webView: https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/ui-android-view/src/main/java/androidx/ui/androidview/WebComponent.kt https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/ui/ui-android-view/integration-tests/android-view-demos/src/main/java/androidx/ui/androidview/demos/WebComponentActivity.kt – Habib Kazemi May 18 '20 at 21:09
-
May you specify this a bit? – nyx69 May 23 '20 at 06:56
-
@pentexnyx I added an answer with more details. – Habib Kazemi Jun 12 '20 at 05:21
-
@HabibKazemi great one (upvoted) - but I was talking to icefex (i didn't mention him though, my bad) – nyx69 Jun 12 '20 at 10:42
4 Answers
There is still no CameraX composable. You need to use AndroidView
to create one.
Updated example for Compose 1.0.0-beta02:
@Composable
fun CameraPreview(
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()
// Preview
val 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
})
}

- 2,632
- 2
- 27
- 35
-
This is along the lines of what I was hoping to find. One question: `ProcessCameraProvider` appears to be undefined. I've included `androidx.camera:camera-lifecycle:1.1.0-alpha3` based on the reference docs [here](https://developer.android.com/reference/androidx/camera/lifecycle/ProcessCameraProvider). What library and version are you using for ProcessCameraProvider? Thanks! – Victor Ude Mar 24 '21 at 19:19
-
1My dependencies are: implementation "androidx.camera:camera-camera2:1.0.0-rc04" implementation "androidx.camera:camera-lifecycle:1.0.0-rc04" implementation "androidx.camera:camera-view:1.0.0-alpha23" in addition to plenty of other androidx dependencies, but I think those should do it. I would imagine that 1.1.0 would work as well, but I haven't tried it. – Sean Mar 25 '21 at 16:22
-
Thanks. I just got it working with `1.1.0-alpha03` from Maven and was on my way back to report. What a time to be alive! :) https://mvnrepository.com/artifact/androidx.camera/camera-lifecycle – Victor Ude Mar 25 '21 at 16:28
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
resId
The id of the layout resource to be inflated. - @param
postInflationCallback
The callback to be invoked after the layout is inflated.
and to access the lifecycle and context we use the ambients
val lifecycleOwner = LifecycleOwnerAmbient.current
val context = 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.PreviewView xmlns: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.
@Composable
fun SimpleCameraPreview() {
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))
}
}
fun bindPreview(
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)
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SimpleCameraPreview()
}
}
}

- 3,438
- 2
- 29
- 47

- 2,172
- 1
- 24
- 30
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'
@Composable
fun CameraPreview(
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 cameraSelector
val 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)
}
}
)
}

- 221
- 1
- 4
- 11
-
thanks this is really helpful while enabling torch without recomposition! – Antonis Radz Aug 21 '23 at 17:22
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
class MainViewModel : ViewModel() {
val imageCapture = ImageCapture.Builder().build()
}
@Composable
fun Main() {
val viewModel: MainViewModel = viewModel()
val imageCapture by remember { viewModel.imageCapture }
CameraPreview(Modifier.fillMaxSize(), imageCapture)
}

- 804
- 7
- 5