在使用Jetpack Compose开发安卓应用,当在学习使用CameraX组件时发现官方提供的教程不是Compose的。教程地址如下:
https://developer.android.com/codelabs/camerax-getting-started?hl=zh-cn#1
与是我就记录一下,简单的示例。
依赖
build.gradle中增加以下内容:
dependencies {
// Camerax implementation
def cameraxVersion = "1.3.1"
implementation ("androidx.camera:camera-core:${cameraxVersion}")
implementation ("androidx.camera:camera-camera2:${cameraxVersion}")
implementation ("androidx.camera:camera-view:${cameraxVersion}")
implementation ("androidx.camera:camera-lifecycle:$cameraxVersion")
// Camerax implementation
}
权限
修改文件CmrXTutorial/app/src/main/AndroidManifest.xml
增加以下内容:
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.CAMERA"/>
修改完以后形如:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.CAMERA"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
...略...
</application>
</manifest>
cmrxtutorial/composables/Camerax.kt
代码如下:
package com.android.example.cmrxtutorial.composables
import android.content.ContentValues
import android.content.Context
import android.os.Build
import android.provider.MediaStore
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@Composable
fun CameraPreviewScreen() {
val lensFacing = CameraSelector.LENS_FACING_BACK
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
val preview = Preview.Builder().build()
val previewView = remember {
PreviewView(context)
}
val cameraxSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
val imageCapture = remember {
ImageCapture.Builder().build()
}
LaunchedEffect(lensFacing) {
val cameraProvider = context.getCameraProvider()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraxSelector, preview, imageCapture)
preview.setSurfaceProvider(previewView.surfaceProvider)
}
Box(contentAlignment = Alignment.BottomCenter, modifier = Modifier.fillMaxSize()) {
AndroidView({ previewView }, modifier = Modifier.fillMaxSize())
Button(onClick = { captureImage(imageCapture, context) }) {
Text(text = "Capture Image")
}
}
}
private fun captureImage(imageCapture: ImageCapture, context: Context) {
val name = "CameraxImage.jpeg"
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
val outputOptions = ImageCapture.OutputFileOptions
.Builder(
context.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
.build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
println("Successs")
}
override fun onError(exception: ImageCaptureException) {
println("Failed $exception")
}
})
}
private suspend fun Context.getCameraProvider(): ProcessCameraProvider =
suspendCoroutine { continuation ->
ProcessCameraProvider.getInstance(this).also { cameraProvider ->
cameraProvider.addListener({
continuation.resume(cameraProvider.get())
}, ContextCompat.getMainExecutor(this))
}
}
cmrxtutorial/MainActivity.kt
代码如下:
package com.android.example.cmrxtutorial
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.content.ContextCompat
import com.android.example.cmrxtutorial.composables.CameraPreviewScreen
import com.android.example.cmrxtutorial.ui.theme.CmrXTutorialTheme
class MainActivity : ComponentActivity() {
private val cameraPermissionRequest =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
setCameraPreview()
} else {
// Camera permission denied
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) -> {
setCameraPreview()
}
else -> {
cameraPermissionRequest.launch(Manifest.permission.CAMERA)
}
}
}
private fun setCameraPreview() {
setContent {
CmrXTutorialTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
CameraPreviewScreen()
}
}
}
}
}