18.4 C
New York
Monday, March 10, 2025

Getting Began with CameraX in Jetpack Compose | by Jolanda Verhoef | Android Builders | Jan, 2025


We’ve heard from you that you just love the ability that each the CameraX and Jetpack Compose libraries offer you, however that you just’d like idiomatic Compose APIs for constructing Digital camera UIs. This yr, our engineering groups labored on two new Compose artifacts, the low-level viewfinder-compose and the high-level camera-compose. Each at the moment are obtainable as alpha releases 🚀🚀🚀.

On this weblog publish collection, we’ll present you learn how to combine the camera-compose APIs in your app. However extra excitingly, we’ll present you a few of the ✨ pleasant UI experiences that integration with Compose unlocks. All of the superb Compose options, like adaptive APIs and animation help, combine seamlessly with the digicam preview!

Right here’s a brief abstract of what every publish will comprise:

  • 🧱 Half 1 (this publish): Constructing a fundamental digicam preview utilizing the brand new camera-compose artifact. We’ll cowl permission dealing with and fundamental integration.
  • 👆 Half 2: Utilizing the Compose gesture system, graphics, and coroutines to implement a visible tap-to-focus.
  • 🔎 Half 3: Exploring learn how to overlay Compose UI components on prime of your digicam preview for a richer person expertise.
  • 📂 Half 4: Utilizing adaptive APIs and the Compose animation framework to easily animate to and from tabletop mode on foldable telephones.

With all of those in motion, our ultimate app will look as follows:

As well as, it is going to easily transfer to and from tabletop mode:

By the tip of this primary publish, you’ll have a useful digicam viewfinder able to be expanded upon within the subsequent components of the collection. Do please code alongside, it’s one of the best ways to be taught!

I’m assuming that you have already got Compose arrange in your app. If you wish to comply with alongside, merely create a brand new app in Android Studio. I usually use the most recent Canary model, as a result of it has the most recent Compose templates (and since I like residing on the sting 😀).

Add the next to your libs.variations.toml file:

[versions]
..
camerax = "1.5.0-alpha03"
accompanist = "0.36.0" # or no matter matches along with your Compose model

[libraries]
..
# Accommodates the essential digicam performance reminiscent of SurfaceRequest
androidx-camera-core = { module = "androidx.digicam:camera-core", model.ref = "camerax" }
# Accommodates the CameraXViewfinder composable
androidx-camera-compose = { module = "androidx.digicam:camera-compose", model.ref = "camerax" }
# Permits us to bind the digicam preview to our UI lifecycle
androidx-camera-lifecycle = { group = "androidx.digicam", identify = "camera-lifecycle", model.ref = "camerax" }
# The precise digicam implementation that renders the preview
androidx-camera-camera2 = { module = "androidx.digicam:camera-camera2", model.ref = "camerax" }
# The helper library to grant the digicam permission
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", model.ref = "accompanist" }

Subsequent, add these to your module construct.gradle.kts dependencies block:

dependencies {
..
implementation(libs.androidx.digicam.core)
implementation(libs.androidx.digicam.compose)
implementation(libs.androidx.digicam.lifecycle)
implementation(libs.androidx.digicam.camera2)
implementation(libs.accompanist.permissions)
}

We added all dependencies in order that we will grant the digicam permission after which truly show the digicam preview. Subsequent, let’s have a look at granting the fitting permission.

The Accompanist permissions library permits us to simply grant the fitting digicam permission. First, we have to arrange the AndroidManifest.xml:



..

Now, we will merely comply with the library’s directions to grant the fitting permission:

class MainActivity : ComponentActivity() {
override enjoyable onCreate(savedInstanceState: Bundle?) {
tremendous.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplicationTheme {
CameraPreviewScreen()
}
}
}
}

@OptIn(ExperimentalPermissionsApi::class)
@Composable
enjoyable CameraPreviewScreen(modifier: Modifier = Modifier) {
val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
if (cameraPermissionState.standing.isGranted) {
CameraPreviewContent(modifier)
} else {
Column(
modifier = modifier.fillMaxSize().wrapContentSize().widthIn(max = 480.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val textToShow = if (cameraPermissionState.standing.shouldShowRationale) {
// If the person has denied the permission however the rationale will be proven,
// then gently clarify why the app requires this permission
"Whoops! Seems like we want your digicam to work our magic!" +
"Don't fret, we simply wanna see your fairly face (and perhaps some cats). " +
"Grant us permission and let's get this social gathering began!"
} else {
// If it is the primary time the person lands on this function, or the person
// would not need to be requested once more for this permission, clarify that the
// permission is required
"Hello there! We'd like your digicam to work our magic! ✨n" +
"Grant us permission and let's get this social gathering began! uD83CuDF89"
}
Textual content(textToShow, textAlign = TextAlign.Middle)
Spacer(Modifier.peak(16.dp))
Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
Textual content("Unleash the Digital camera!")
}
}
}
}

@Composable
non-public enjoyable CameraPreviewContent(modifier: Modifier = Modifier) {
// TODO: Implement
}

With this, we get a pleasant UI that permits the person to grant the digicam permission earlier than exhibiting the digicam preview:

It’s good follow to separate our enterprise logic from our UI. We will do that by making a view mannequin for our display. This view mannequin units up the CameraX Preview use case. Please observe that use instances in CameraX signify configurations of assorted workflows one can implement with the library, i.e. previewing, capturing, recording, and analyzing. The view mannequin additionally binds the UI to the digicam supplier:

class CameraPreviewViewModel : ViewModel() {
// Used to arrange a hyperlink between the Digital camera and your UI.
non-public val _surfaceRequest = MutableStateFlow(null)
val surfaceRequest: StateFlow = _surfaceRequest

non-public val cameraPreviewUseCase = Preview.Builder().construct().apply {
setSurfaceProvider { newSurfaceRequest ->
_surfaceRequest.replace { newSurfaceRequest }
}
}

droop enjoyable bindToCamera(appContext: Context, lifecycleOwner: LifecycleOwner) {
val processCameraProvider = ProcessCameraProvider.awaitInstance(appContext)
processCameraProvider.bindToLifecycle(
lifecycleOwner, DEFAULT_FRONT_CAMERA, cameraPreviewUseCase
)

// Cancellation alerts we're executed with the digicam
attempt { awaitCancellation() } lastly { processCameraProvider.unbindAll() }
}
}

There’s fairly a bit happening right here! The code defines a CameraPreviewViewModel class, chargeable for managing the digicam preview. It makes use of the CameraX Preview builder to configure how the preview needs to be certain to the UI. The bindToCamera operate initializes the digicam, binds to the supplied LifecycleOwner in order that the digicam solely runs when the lifecycle is at the least began, and begins the preview stream.

The digicam, which is a part of the internals of the digicam libraries, must render to the floor that’s supplied by the UI. So the library must have a solution to request a floor. That’s precisely what the SurfaceRequest is for! So every time the digicam signifies it wants a floor, a surfaceRequest is triggered. You then ahead that request to the UI, the place it may well move the floor to the request object.

Lastly, we wait till the UI is finished binding to the digicam, and be sure that we launch the digicam sources to keep away from leaks.

Now that we have now a view mannequin, we will implement our CameraPreviewContent composable. It reads the floor request from the view mannequin, binds to the digicam whereas the composable is within the composition tree, and calls the CameraXViewfinder from the library:

@Composable
enjoyable CameraPreviewContent(
viewModel: CameraPreviewViewModel,
modifier: Modifier = Modifier,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.present
) {
val surfaceRequest by viewModel.surfaceRequest.collectAsStateWithLifecycle()
val context = LocalContext.present
LaunchedEffect(lifecycleOwner) {
viewModel.bindToCamera(context.applicationContext, lifecycleOwner)
}

surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
modifier = modifier
)
}
}

As talked about within the earlier part, the surfaceRequest permits the digicam library to request a floor when it wants one to render to. On this piece of code, we gather these surfaceRequest cases and ahead them to the CameraXViewfinder, which is a part of the camera-compose artifact.

And with that, we have now a working full display viewfinder! You’ll find the complete code snippet right here. Within the subsequent weblog publish, we’ll add a easy tabletop mode by listening to the gadget’s show options, and by utilizing Compose animations to go to and from tabletop mode. Keep tuned!

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles