Compose笔记(七十六)--拍照预览

这一节主要了解一下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("重新拍照")
                }
            }
        }
    }
}

效果:

相关推荐
ZC跨境爬虫2 小时前
dankoe视频笔记:如何培养对自己喜欢之事的痴迷感
人工智能·笔记·搜索引擎
新手小新2 小时前
C#学习笔记1-在VS CODE部署C#开发环境
笔记·学习·c#
ZC跨境爬虫3 小时前
Dan koe视频笔记: 个人成长与目标设定的重要性
人工智能·笔记·搜索引擎
小陈的进阶之路4 小时前
Appium 自动化测试笔记
笔记·appium
yy_xzz5 小时前
【Qt 开发笔记】能扛住断电、多线程的通用配置类(移植直接用)
笔记·qt
我不是懒洋洋5 小时前
AI的影响8
笔记
资深流水灯工程师5 小时前
FREERTOS的核心内容与核心组件
笔记
xian_wwq6 小时前
【学习笔记】GB/T 20986-2023 详解,10 类网络安全事件分类
笔记·学习·web安全
鱼鳞_6 小时前
Java学习笔记_Day27(Stream流)
java·笔记·学习