用ConcurrentCamera实现手机前后镜头同时浏览

从Android 11开始,Google推出两个很有意思的类:ConcurrentCamera和ConcurrentCamera.SingleCameraConfig,利用这两个类可以轻松实现在屏幕上同时观看手机前后两个镜头的画面,以往这个功能需要非常复杂的代码才能实现。ConcurrentCamera通常翻译成并发相机。
为了判断系统是否支持并发相机功能,可以使用hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT)。
废话少说,直接上代码。现在的android studio默认建立的系统都是Jetpack Compose框架,语言当然是kotlin。习惯了Java类实现框架的程序员转到Compose框架,需要有个学习适应过程,好在学习曲线并不陡峭。
MainActivity.kt

package com.example.dual_camera

import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.dual_camera.ui.theme.DualCameraTheme

class MainActivity : ComponentActivity() {
    private val localContext = staticCompositionLocalOf<Context> {
        error("No context provided")
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (checkSelfPermission(android.Manifest.permission.CAMERA) !=
            PackageManager.PERMISSION_GRANTED
        ) {
            requestPermissionLauncher.launch(android.Manifest.permission.CAMERA)
        }

        enableEdgeToEdge()
        setContent {
            CompositionLocalProvider(localContext provides this){
                DualCameraTheme {
                    Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                        // 检查系统是否支持并发相机
                        if (this.packageManager
                                .hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT)) {
                            ConcurrentCameraScreen()
                        } else {
                            Greeting(
                                name = "并发相机",
                                modifier = Modifier.padding(innerPadding)
                            )
                        }
                    }
                    // 显示权限授予对话框
                    if (showGrantedDialog.value) {
                        GrantedDialog(
                            onDismiss = { showGrantedDialog.value = false }
                        )
                    }
                    // 显示权限拒绝对话框
                    if (showDeniedDialog.value) {
                        DeniedDialog(
                            onDismiss = {
                                showDeniedDialog.value = false
                                finish()    // 关闭应用
                            }
                        )
                    }
                }
            }
        }
    }

    private val showGrantedDialog = mutableStateOf(false)
    private val showDeniedDialog = mutableStateOf(false)

    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            // 权限已授予,可执行相关操作
            showGrantedDialog.value = true
        } else {
            // 权限被拒绝,可提示用户
            showDeniedDialog.value = true
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "系统不支持 $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    DualCameraTheme {
        Greeting("Android")
    }
}

@Composable
private fun GrantedDialog(onDismiss: () -> Unit) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("权限授予") },
        text = { Text("相机权限已成功授予,可以使用相机功能。") },
        confirmButton = {
            Button(onClick = onDismiss) {
                Text("确定")
            }
        }
    )
}

@Composable
private fun DeniedDialog(onDismiss: () -> Unit) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("权限拒绝") },
        text = { Text("相机权限被拒绝,可能无法使用相机功能。") },
        confirmButton = {
            Button(onClick = onDismiss) {
                Text("确定")
            }
        }
    )
}

程序中首先处理相机权限,此处不需赘言。在setContent中首先确定系统是否支持并发相机功能,如果支持则打开ConcurrentCameraScreen()。

ConcurrentCameraScreen.kt

package com.example.dual_camera

import android.util.Log
import android.util.Size
import androidx.camera.core.CameraSelector
import androidx.camera.core.ConcurrentCamera
import androidx.camera.core.ConcurrentCamera.SingleCameraConfig
import androidx.camera.core.Preview
import androidx.camera.core.UseCaseGroup
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.LocalLifecycleOwner

@Suppress("DEPRECATION")
@Composable
fun ConcurrentCameraScreen() {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    // 建立PreviewView
    val frontPreviewView = remember { PreviewView(context) }
    val backPreviewView = remember { PreviewView(context) }
    // 初始化CameraProvider的监听器
    val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
    // 初始化CameraProvider和ConcurrentCamera
    var cameraProvider by remember { mutableStateOf<ProcessCameraProvider?>(null) }
    var concurrentCamera by remember { mutableStateOf<ConcurrentCamera?>(null) }

    LaunchedEffect(cameraProviderFuture) {
        // 监听CameraProvider的变化,从监听器中获取CameraProvider实例
        cameraProvider = cameraProviderFuture.get()
    }

    DisposableEffect(lifecycleOwner, cameraProvider) {
        // 如果CameraProvider为空,则不执行任何操作
        val cameraProvider = cameraProvider ?: return@DisposableEffect onDispose {}
        //定义前后摄像头的选择器
        val frontCameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
        val backCameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
        // 创建前后摄像头的预览用例,并将其绑定到对应的PreviewView上
        val frontPreview = Preview.Builder().setTargetResolution(Size(640, 480)).build()
            .also { it.setSurfaceProvider(frontPreviewView.surfaceProvider) }
        val backPreview = Preview.Builder().setTargetResolution(Size(640, 480)).build()
            .also { it.setSurfaceProvider(backPreviewView.surfaceProvider) }
        // 创建前后摄像头的配置
        val frontSingleCameraConfig = SingleCameraConfig(
            frontCameraSelector,
            UseCaseGroup.Builder().addUseCase(frontPreview).build(),
            lifecycleOwner
        )
        val backSingleCameraConfig = SingleCameraConfig(
            backCameraSelector,
            UseCaseGroup.Builder().addUseCase(backPreview).build(),
            lifecycleOwner
        )
        try {
            // 解除既有绑定
            cameraProvider.unbindAll()
            // 绑定前后摄像头的配置,建立concurrentCamera
            cameraProvider.bindToLifecycle(
                listOf(
                    frontSingleCameraConfig, backSingleCameraConfig
                )
            )
        } catch (e: Exception) {
            Log.e("ConcurrentCameraScreen", "Failed to bind cameras", e)
            e.printStackTrace()
        }
        onDispose {
            concurrentCamera = null
            cameraProvider.unbindAll()
        }
    }

    Column(modifier = Modifier.fillMaxSize()) {
        AndroidView(
            factory = { frontPreviewView },
            modifier = Modifier
                .weight(1f)
                .align(Alignment.CenterHorizontally)
        )
        AndroidView(
            factory = { backPreviewView },
            modifier = Modifier
                .weight(1f)
                .align(Alignment.CenterHorizontally)
        )
    }
}

程序中的每个步骤都有注释,请参考注释。至于compose架构中的各个元素,请参考相关教程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值