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("重新拍照")
                }
            }
        }
    }
}

效果:

相关推荐
zjxtxdy37 分钟前
SPI通信协议
笔记·单片机
彧azz2 小时前
Linux 网络编程学习总结
linux·网络·笔记·学习·面试
aramae3 小时前
MySQL内置函数(7)
开发语言·笔记·后端·mysql·其他
bessyon3 小时前
信息透明度与决策效率:一个技术视角的分析
笔记
传奇开心果编程4 小时前
【Jetpack Compose基础语法学与练】第8课 rememberSaveable,页面旋转/系统重建保留状态
android·学习·ui·kotlin·android jetpack
hanlin034 小时前
刷题笔记:力扣第41题-缺失的第一个正数
笔记·算法·leetcode
自不量力的A同学5 小时前
开源一站式智能终端 uniTerm v1.9.4 发布
笔记
91刘仁德6 小时前
C++ 继承和多态 设计模式
c语言·c++·笔记
库玛西7 小时前
哈夫曼树与前缀编码:数据压缩的贪心核心
c语言·c++·笔记·考研
传奇开心果编程8 小时前
【Jetpack Compose基础语法学与练】第7课 if条件渲染 + 列表基础(LazyColumn)
学习·ui·kotlin·android jetpack