从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架构中的各个元素,请参考相关教程。
9377

被折叠的 条评论
为什么被折叠?



