这一节主要了解一下Compose中使用拍照预览功能,在应用开发过程中,经常会用到这个拍照预览功能,其中主要用到是Android camera相关的依赖库。简单总结:
添加依赖:
Kotlin
dependencies {
// CameraX
implementation("androidx.camera:camera-core:1.3.0")
implementation("androidx.camera:camera-camera2:1.3.0")
implementation("androidx.camera:camera-lifecycle:1.3.0")
implementation("androidx.camera:camera-view:1.3.0")
// 权限
implementation("com.google.accompanist:accompanist-permissions:0.34.0")
}
AndroidManifest.xml权限
Kotlin
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
Kotlin
import android.content.ContentValues
import android.net.Uri
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.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionState
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import java.text.SimpleDateFormat
import java.util.Locale
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraCaptureScreenDemo() {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
val cameraPermission: PermissionState = rememberPermissionState(
android.Manifest.permission.CAMERA
)
var capturedImageUri by remember { mutableStateOf<Uri?>(null) }
var imageCapture: ImageCapture? by remember { mutableStateOf(null) }
LaunchedEffect(Unit) {
if (!cameraPermission.status.isGranted) {
cameraPermission.launchPermissionRequest()
}
}
// 权限未授予时显示提示
if (!cameraPermission.status.isGranted) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("请开启相机权限以使用拍照功能")
}
return
}
// 拍照并保存到相册
fun takePhoto() {
val capture = imageCapture ?: return
val contentValues = ContentValues().apply {
val fileName = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault())
.format(System.currentTimeMillis())
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
}
val outputOptions = ImageCapture.OutputFileOptions.Builder(
context.contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
capture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
capturedImageUri = outputFileResults.savedUri
}
override fun onError(ex: ImageCaptureException) {
capturedImageUri = null
}
}
)
}
Box(modifier = Modifier.fillMaxSize()) {
AndroidView(
factory = { ctx ->
val previewView = PreviewView(ctx).apply {
scaleType = PreviewView.ScaleType.FILL_CENTER
}
val preview = Preview.Builder().build().apply {
setSurfaceProvider(previewView.surfaceProvider)
}
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
.build()
val cameraProvider = cameraProviderFuture.get()
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture
)
} catch (e: Exception) {
e.printStackTrace()
}
previewView
},
modifier = Modifier.fillMaxSize()
)
Button(
onClick = { takePhoto() },
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(50.dp)
) {
Text("拍照")
}
capturedImageUri?.let { uri ->
val bitmap = remember(uri) {
context.contentResolver.openInputStream(uri)?.use { inputStream ->
android.graphics.BitmapFactory.decodeStream(inputStream)
}
}
bitmap?.let { bmp ->
Image(
bitmap = bmp.asImageBitmap(),
contentDescription = "Captured photo",
modifier = Modifier.fillMaxSize()
)
Button(
onClick = { capturedImageUri = null },
modifier = Modifier
.align(Alignment.TopCenter)
.padding(20.dp)
) {
Text("重新拍照")
}
}
}
}
}
效果:
