如何在Jetpack Compose中集成Camera2 API调节ISO、快门速度与AWB
在Jetpack Compose中集成Camera2 API实现手动相机参数调节
1. Compose环境下初始化Camera2 API
Camera2的核心逻辑仍依赖Android原生组件(如CameraManager、CameraDevice),Compose仅负责UI层。建议将相机初始化、管理逻辑封装在ViewModel中,避免在Composable中处理复杂生命周期操作。
核心实现步骤
- 在ViewModel中获取
CameraManager实例,遍历并选择目标相机(优先后置) - 处理相机权限请求(Android 13+需额外申请
POST_NOTIFICATIONS) - 打开相机设备,创建
CameraCaptureSession并绑定预览Surface
ViewModel核心代码
class CameraViewModel : ViewModel() { private lateinit var cameraManager: CameraManager private var cameraDevice: CameraDevice? = null private var captureSession: CameraCaptureSession? = null private var previewRequestBuilder: CaptureRequest.Builder? = null private var previewSurface: Surface? = null fun initCameraManager(context: Context) { cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager } fun openCamera(cameraId: String) { cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() { override fun onOpened(device: CameraDevice) { cameraDevice = device startPreview() } override fun onDisconnected(device: CameraDevice) { device.close() cameraDevice = null } override fun onError(device: CameraDevice, error: Int) { device.close() cameraDevice = null } }, Handler(Looper.getMainLooper())) } fun setPreviewSurface(surface: Surface) { previewSurface = surface cameraDevice?.let { startPreview() } } private fun startPreview() { val device = cameraDevice ?: return val surface = previewSurface ?: return try { previewRequestBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) previewRequestBuilder?.addTarget(surface) device.createCaptureSession( listOf(surface), object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) { captureSession = session updatePreview() } override fun onConfigureFailed(session: CameraCaptureSession) { // 处理配置失败逻辑 } }, Handler(Looper.getMainLooper()) ) } catch (e: CameraAccessException) { e.printStackTrace() } } private fun updatePreview() { val session = captureSession ?: return val request = previewRequestBuilder?.build() ?: return session.setRepeatingRequest(request, null, Handler(Looper.getMainLooper())) } override fun onCleared() { super.onCleared() captureSession?.close() cameraDevice?.close() } }
Compose中绑定预览Surface
通过AndroidView桥接TextureView,为Camera2提供预览载体:
@Composable fun CameraPreview(viewModel: CameraViewModel) { AndroidView( factory = { context -> TextureView(context).apply { surfaceTextureListener = object : TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { viewModel.setPreviewSurface(Surface(surface)) } override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {} override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean { viewModel.setPreviewSurface(Surface(surface)) return true } override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {} } } }, modifier = Modifier.fillMaxSize() ) }
2. 编程方式调节ISO、快门速度与AWB
通过修改CaptureRequest.Builder的参数实现调节,每次修改后更新预览请求:
核心参数与调节方法(ViewModel中添加)
// 调节ISO(需在相机支持范围内) fun setIso(iso: Int) { previewRequestBuilder?.set(CaptureRequest.SENSOR_SENSITIVITY, iso) updatePreview() } // 调节快门速度(单位:纳秒) fun setExposureTime(exposureTime: Long) { previewRequestBuilder?.set(CaptureRequest.SENSOR_EXPOSURE_TIME, exposureTime) updatePreview() } // 锁定手动AWB fun lockAwb() { previewRequestBuilder?.apply { set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF) set(CaptureRequest.CONTROL_AWB_LOCK, true) } updatePreview() } // 恢复自动AWB fun unlockAwb() { previewRequestBuilder?.apply { set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO) set(CaptureRequest.CONTROL_AWB_LOCK, false) } updatePreview() } // 获取相机支持的参数范围 fun getCameraCharacteristics(cameraId: String): CameraCharacteristics? { return try { cameraManager.getCameraCharacteristics(cameraId) } catch (e: CameraAccessException) { null } }
参数范围说明
- ISO范围:从
CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE获取 - 快门速度范围:从
CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE获取 - AWB手动调节:需先关闭自动模式,再通过
CONTROL_AWB_GAINS调节色温或直接锁定当前值
3. 参数调节的Compose UI最佳实践
布局原则
- 全屏预览作为背景,参数控件置于底部/侧边
- 使用
LaunchedEffect处理权限请求与相机初始化 - 通过
rememberViewModel确保ViewModel生命周期与Composable一致
示例UI代码
@Composable fun CameraControlScreen() { val viewModel: CameraViewModel = viewModel() val context = LocalContext.current val permissionState = rememberPermissionState(android.Manifest.permission.CAMERA) LaunchedEffect(Unit) { viewModel.initCameraManager(context) if (!permissionState.hasPermission) { permissionState.launchPermissionRequest() } else { // 获取后置摄像头ID val cameraId = cameraManager.cameraIdList.firstOrNull { id -> val characteristics = cameraManager.getCameraCharacteristics(id) characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK } cameraId?.let { viewModel.openCamera(it) } } } Box(modifier = Modifier.fillMaxSize()) { CameraPreview(viewModel = viewModel) // 参数调节面板 Column( modifier = Modifier .align(Alignment.BottomCenter) .fillMaxWidth() .background(Color.Black.copy(alpha = 0.7f)) .padding(16.dp) ) { // ISO调节滑块 val cameraId = cameraManager.cameraIdList.firstOrNull() ?: "" val isoRange = viewModel.getCameraCharacteristics(cameraId) ?.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE) ?: IntRange(100, 1600) var currentIso by remember { mutableStateOf(isoRange.first) } Text(text = "ISO: $currentIso", color = Color.White) Slider( value = currentIso.toFloat(), onValueChange = { currentIso = it.toInt() viewModel.setIso(currentIso) }, valueRange = isoRange.first.toFloat()..isoRange.last.toFloat(), colors = SliderDefaults.colors(thumbColor = Color.White, activeTrackColor = Color.White) ) // 快门速度调节滑块 val exposureRange = viewModel.getCameraCharacteristics(cameraId) ?.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE) ?: LongRange(100000, 10000000) var currentExposure by remember { mutableStateOf(exposureRange.first) } val exposureMs = (currentExposure / 1000000).toFloat() Text(text = "快门速度: ${String.format("%.1f", exposureMs)}ms", color = Color.White) Slider( value = exposureMs, onValueChange = { currentExposure = (it * 1000000).toLong() viewModel.setExposureTime(currentExposure) }, valueRange = (exposureRange.first / 1000000).toFloat()..(exposureRange.last / 1000000).toFloat(), colors = SliderDefaults.colors(thumbColor = Color.White, activeTrackColor = Color.White) ) // AWB控制按钮 Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { Button( onClick = { viewModel.lockAwb() }, colors = ButtonDefaults.buttonColors(backgroundColor = Color.White) ) { Text(text = "锁定AWB") } Button( onClick = { viewModel.unlockAwb() }, colors = ButtonDefaults.buttonColors(backgroundColor = Color.White) ) { Text(text = "自动AWB") } } } } }
关键注意事项
- 权限校验:必须在获取相机权限后再初始化相机
- 参数合法性:所有手动调节参数需落在相机支持范围内,避免抛出异常
- 线程安全:Camera2操作需在主线程或指定Handler线程执行,示例中使用主线程回调
- 内存泄漏:ViewModel销毁时必须关闭相机设备与捕获会话
内容的提问来源于stack exchange,提问作者Akshar Kalathiya
相关产品推荐
相关产品推荐

