Skip to content

CameraX 相机库

源:CameraX 概览

CameraX 是一个 Google 推出的 Jetpack 支持库,旨在帮助您简化相机应用的开发。它提供了一套一致且易于使用的 API,适用于大多数 Android 设备,并向后兼容至 Android 5.0(API 级别 21)。

核心优势

  • 易用性:只需几行代码即可实现预览、拍照和视频录制。
  • 一致性:自动处理不同设备上的兼容性问题(如长宽比、方向、旋转)。
  • 扩展性:支持人像模式、HDR、夜间模式等扩展功能(取决于设备支持)。

1. 添加依赖

kotlin
[versions]
camerax = "1.3.4"

[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-video = { group = "androidx.camera", name = "camera-video", version.ref = "camerax" }
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "camerax" }
androidx-camera-extensions = { group = "androidx.camera", name = "camera-extensions", version.ref = "camerax" }
kotlin
dependencies {
    implementation(libs.androidx.camera.core)
    implementation(libs.androidx.camera.camera2)
    implementation(libs.androidx.camera.lifecycle)
    implementation(libs.androidx.camera.video)
    implementation(libs.androidx.camera.view)
    implementation(libs.androidx.camera.extensions)
}

2. 核心用例 (Use Cases)

CameraX 的工作流程基于“用例”。常见的用例有:

  • Preview: 显示相机实时预览。
  • ImageCapture: 拍摄并保存照片。
  • ImageAnalysis: 实时处理帧数据(如二维码识别)。
  • VideoCapture: 录制视频。

3. 实现相机预览

权限注意

请确保在 AndroidManifest.xml 中声明了 android.permission.CAMERA 权限,并在运行时请求。

第一步:添加 PreviewView

在 XML 布局中添加相机预览容器:

xml
<androidx.camera.view.PreviewView
    android:id="@+id/viewFinder"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

第二步:启动相机逻辑

kotlin
private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        // 用于将相机生命周期绑定到 LifecycleOwner
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        // 1. 配置 Preview 用例
        val preview = Preview.Builder().build().also {
            it.setSurfaceProvider(binding.viewFinder.surfaceProvider)
        }

        // 2. 配置 ImageCapture 用例
        imageCapture = ImageCapture.Builder().build()

        // 选择后置摄像头
        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

        try {
            // 绑定前先解除所有绑定
            cameraProvider.unbindAll()

            // 将用例绑定到生命周期
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageCapture
            )

        } catch(exc: Exception) {
            Log.e(TAG, "绑定失败", exc)
        }

    }, ContextCompat.getMainExecutor(this))
}

4. 拍摄照片

kotlin
private fun takePhoto() {
    val imageCapture = imageCapture ?: return

    // 创建保存位置
    val name = SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(System.currentTimeMillis())
    val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, name)
        put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
    }

    val outputOptions = ImageCapture.OutputFileOptions
        .Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
        .build()

    // 执行拍摄
    imageCapture.takePicture(
        outputOptions,
        ContextCompat.getMainExecutor(this),
        object : ImageCapture.OnImageSavedCallback {
            override fun onError(exc: ImageCaptureException) {
                Log.e(TAG, "拍摄失败: \${exc.message}", exc)
            }

            override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                val msg = "照片保存成功: \${output.savedUri}"
                Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
            }
        }
    )
}

5. 图像分析 (ImageAnalysis)

用于实时扫描码或进行人脸检测。

kotlin
val imageAnalysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()

imageAnalysis.setAnalyzer(executor) { imageProxy ->
    val rotationDegrees = imageProxy.imageInfo.rotationDegrees
    // 在这里处理图像数据
    // 处理完成后务必释放
    imageProxy.close()
}