关于 camera1-to-camerax
帮助开发者将 Android 相机代码从旧版 Camera API 迁移到 CameraX,涵盖生命周期绑定、预览、对焦、拍照及切换镜头,并分别说明 Views 和 Compose 的做法。
主要功能
- 01配置 CameraX 依赖
- 02移除旧版相机代码
- 03绑定生命周期用例
- 04Views 与 Compose 预览
- 05点击对焦与拍照
- 06切换前后摄像头
适用场景
- 01将应用从 android.hardware.Camera 迁移
- 02为 Views 或 Compose 添加 CameraX 预览
- 03实现点击对焦和拍照
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ android/ skills --skill camera1-to-camerax安装后在 Claude Code 中输入/camera1-to-camerax确认已加载。
详细介绍
第 0 步:添加依赖
检查并添加所需的 CameraX 依赖项。为实现互操作性,请使用 1.3.0 或更高版本;如需使用 Compose 扩展,请使用 1.5.0 或更高版本。
如果使用版本目录(libs.versions.toml),请添加以下内容:
[versions]
camerax = "<minimum_version_needed>"
[libraries]
androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "camerax" }
androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "camerax" }
androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "camerax" }
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "camerax" }
androidx-camera-compose = { group = "androidx.camera", name = "camera-compose", version.ref = "camerax" }
并在 build.gradle.kts(或 build.gradle)中添加:
implementation(libs.androidx.camera.core)
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.view)
implementation(libs.androidx.camera.compose)
如果不使用版本目录,则使用以下标准 Gradle 依赖项:
implementation "androidx.camera:camera-core:<minimum_version_needed>"
implementation "androidx.camera:camera-camera2:<minimum_version_needed>"
implementation "androidx.camera:camera-lifecycle:<minimum_version_needed>"
implementation "androidx.camera:camera-view:<minimum_version_needed>"
implementation "androidx.camera:camera-compose:<minimum_version_needed>"
第 1 步:移除旧版实现
- 删除所有
android.hardware.Camera实例。 - 删除
SurfaceView和SurfaceHolder.Callback实现(surfaceCreated、surfaceChanged、surfaceDestroyed)。 - 移除在
onResume或onPause中打开或释放相机的自定义生命周期处理逻辑。 - 移除手动计算方向的矩阵。
第 2 步:初始化 ProcessCameraProvider
请求 ProcessCameraProvider,并将用例绑定到 Activity 或 Fragment 生命周期。
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(context, lifecycleOwner) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
cameraProvider.unbindAll() // Unbind before rebinding
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageCapture
)
val cameraControl = camera.cameraControl
}, ContextCompat.getMainExecutor(context)
)
}
第 3 步:实现预览和点击对焦
根据应用的 UI 工具包,准确选择以下一种模式:
选项 A:适用于 Android Views(旧版 XML)
使用 androidx.camera.view.PreviewView。
1. 设置预览:
preview.setSurfaceProvider(previewView.surfaceProvider)
2. 处理点击对焦:
val factory = previewView.meteringPointFactory
val point = factory.createPoint(x, y) // x, y from touch event
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build()
cameraControl?.startFocusAndMetering(action)
选项 B:适用于 Jetpack Compose
使用 androidx.camera.compose.CameraXViewfinder。
1. 设置预览和 SurfaceRequest:
var surfaceRequest by remember { mutableStateOf<SurfaceRequest?>(null) }
val preview = remember {
Preview.Builder().build().apply {
setSurfaceProvider { request -> surfaceRequest = request }
}
}
2. 渲染取景器:
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
coordinateTransformer = coordinateTransformer,
modifier = Modifier
)
}
3. 在 Compose 中处理点击对焦:
// Inside your tap gesture handler...
val surfaceCoords = with(coordinateTransformer) { offset.transform() }
val factory = SurfaceOrientedMeteringPointFactory(
request.resolution.width.toFloat(),
request.resolution.height.toFloat()
)
val point = factory.createPoint(surfaceCoords.x, surfaceCoords.y)
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build()
cameraControl?.startFocusAndMetering(action)
4. 在 Compose 中更新目标旋转角度:
LaunchedEffect(configuration) {
if (!view.isInEditMode) {
val rotation = view.display?.rotation ?: Surface.ROTATION_0
imageCapture.targetRotation = rotation
preview.targetRotation = rotation
}
}
第 4 步:拍摄照片
使用 ImageCapture 用例拍照。ImageProxy 会直接处理旋转。
imageCapture.takePicture(
cameraExecutor,
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
// Adjust rotation natively via ImageProxy
val matrix = Matrix()
matrix.postRotate(image.imageInfo.rotationDegrees.toFloat())
if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
matrix.postScale(-1f, 1f) // Mirror for front camera
}
val rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
// MUST close proxy
image.close()
}
override fun onError(exception: ImageCaptureException) {
Log.e("CameraX", "Capture failed: ${exception.message}", exception)
}
}
)
第 5 步:切换相机
要在前后摄像头之间切换,请更改 CameraSelector 并重新触发 ProcessCameraProvider 逻辑。
lensFacing = if (lensFacing == CameraSelector.LENS_FACING_BACK) {
CameraSelector.LENS_FACING_FRONT
} else {
CameraSelector.LENS_FACING_BACK
}
限制
- 不要手动管理相机生命周期:通过
ProcessCameraProvider将相机绑定到LifecycleOwner。避免在onResume或onPause中手动打开或关闭相机。 - 不要手动计算对焦矩阵:
MeteringPointFactory会处理坐标转换,包括设备旋转偏移。避免自行实现矩阵。 - 不要忘记关闭 ImageProxy:请记得在拍摄回调中调用
image.close()。跳过此调用会锁定拍摄管线,并中断后续拍照。 - 不要在 Compose 代码中使用 AndroidView 包装
PreviewView:对于 Compose UI 布局,请使用CameraXViewfinder。在AndroidView中编译PreviewView是一种较旧的备用方案,会导致尺寸调整问题。
评论
支持 Markdown