架构

结构

使用CameraX,借助名为“用例”的抽象概念与设备的相机进行交互。不同的用例可以组合使用,也可以同时处于活跃状态。
目前提供的用例有:
  • 预览:接受用于显示预览的 Surface,例如 PreviewView
  • 图片分析:为分析(例如机器学习)提供 CPU 可访问的缓冲区。
  • 图片拍摄:拍摄并保存照片。

API模型

使用CameraX,需指定一下内容:
  • 具有配置选项的所需用例。set()方法配置用例,build()方法完成这些用例。每个用例对象都提供一组特定于该用例的API,例如,图片拍摄用例会提供 takePicture()方法调用。
  • 通过附加监听器来指定如何处理输出数据。
示例
val preview = Preview.Builder().build() val viewFinder: PreviewView = findViewById(R.id.previewView) // The use case is bound to an Android Lifecycle with the following code val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // PreviewView creates a surface provider and is the recommended provider preview.setSurfaceProvider(viewFinder.getSurfaceProvider())

生命周期

不支持组合用例可以执行以下操作之一:
  • 将兼容的用例划分到多个 Fragment 中,然后在 Fragment 之间进行切换
  • 创建自定义生命周期组件并使用该组件来手动控制相机生命周期
如果您将视图用例和相机用例的生命周期所有者分开(例如,如果您使用自定义生命周期或保留 Fragment),则必须通过使用 ProcessCameraProvider.unbindAll() 或分别取消绑定各个用例来确保所有用例均未绑定 CameraX。或者,当您将用例绑定到生命周期时,可以让 CameraX 管理拍摄会话的开启和关闭操作以及用例的取消绑定操作。

自定义LifecycleOwner

对于高级用例,可创建自定义的LifecycleOwner,以显式控制CameraX会话生命周期,而不是将其绑定到标准的Android LifecycleOwner。

并发用例

多个用例可以同时运行。最好通过对CameraProcessProvider.bindToLifecycle()的一次调用来绑定所有用例。(最佳实践处理配置更改
示例 - 创建并同时运行两个用例,并指定同时用于这两个用例的生命周期
private lateinit var imageCapture: ImageCapture override fun onCreate(savedInstanceState: Bundle?) {     super.onCreate(savedInstanceState)     setContentView(R.layout.activity_main)     val cameraProviderFuture = ProcessCameraProvider.getInstance(this)     cameraProviderFuture.addListener(Runnable {         // Camera provider is now guaranteed to be available         val cameraProvider = cameraProviderFuture.get()         // Set up the preview use case to display camera preview.         val preview = Preview.Builder().build()         // Set up the capture use case to allow users to take photos.         imageCapture = ImageCapture.Builder()                 .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)                 .build()         // Choose the camera by requiring a lens facing         val cameraSelector = CameraSelector.Builder()                 .requireLensFacing(CameraSelector.LENS_FACING_FRONT)                 .build()         // Attach use cases to the camera with the same lifecycle owner         val camera = cameraProvider.bindToLifecycle(                 this as LifecycleOwner, cameraSelector, preview, imageCapture)         // Connect the preview use case to the previewView         preview.setSurfaceProvider(                 previewView.getSurfaceProvider())     }, ContextCompat.getMainExecutor(this)) }
保证支持的配置组合(当单独需要“预览”或“视频拍摄”用例时
预览或视频拍摄
图片拍摄
分析
说明
向用户提供预览或录制视频、拍摄照片以及分析图片流。
拍摄照片并分析图片流。
向用户提供预览或录制视频以及拍摄照片。
向用户提供预览或录制视频以及分析图片流。
同时需要“预览”和“视频拍摄”用例,则可以附条件地支持的用例组合
预览
视频拍摄
图片拍摄
分析
特殊要求
保证适用于所有相机
LIMITED(或更好)的相机设备。
LEVEL_3(或更好)的相机设备。
另:
  • 每个用例都可以单独使用。例如,应用可以在不使用预览的情况下录制视频。
  • 启用扩展后,只能保证能够使用 ImageCapture 和 Preview 的组合。根据 OEM 实现情况,可能无法同时添加 ImageAnalysis;无法为 VideoCapture 用例启用扩展。如需了解详情,请参阅扩展参考文档
  • 对于某些相机而言,在较低分辨率模式下可以支持的组合,在较高的分辨率下将无法支持,这具体取决于相机的功能。
所支持的硬件级别可以从 Camera2CameraInfo 中检索。例如,以下代码可检查默认的后置摄像头是否是 LEVEL_3 设备:
@androidx.annotation.OptIn(ExperimentalCamera2Interop::class) fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean {     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {         return CameraSelector.DEFAULT_BACK_CAMERA             .filter(cameraProvider.availableCameraInfos)             .firstOrNull()             ?.let { Camera2CameraInfo.from(it) }             ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3     }     return false }
注意:如果创建的用例组合不兼容,则在首次调用 createCaptureSession() 时会抛出运行时错误。如果将其他用例添加到正在运行的会话中,可能需要进行重新配置,这可能会导致可见的故障。

权限

需要 CAMERA 权限。
如需将图片保存到文件中,除非所用设备搭载 Android 10 或更高版本,否则应用还需要 WRITE_EXTERNAL_STORAGE 权限。

要求

CameraX 具有以下最低版本要求:
  • Android API 级别 21
  • Android 架构组件 1.1.1
对于能够感知生命周期的 Activity,请使用 FragmentActivity 或 AppCompatActivity

依赖

  1. Google Maven代码库
// 项目的settings.gradle dependencyResolutionManagement {     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)     repositories { google() // 添加google()代码库         mavenCentral()     } }
  1. 将以下内容添加到 Android 代码块的末尾
android {     compileOptions {         sourceCompatibility JavaVersion.VERSION_1_8         targetCompatibility JavaVersion.VERSION_1_8     }     // For Kotlin projects     kotlinOptions {         jvmTarget = "1.8"     } }
  1. 将以下内容添加到应用的每个模块的 build.gradle 文件中
dependencies {   // CameraX core library using the camera2 implementation   def camerax_version = "1.1.0-beta02"   // The following line is optional, as the core library is included indirectly by camera-camera2   implementation "androidx.camera:camera-core:${camerax_version}"   implementation "androidx.camera:camera-camera2:${camerax_version}"   // If you want to additionally use the CameraX Lifecycle library   implementation "androidx.camera:camera-lifecycle:${camerax_version}"   // If you want to additionally use the CameraX VideoCapture library   implementation "androidx.camera:camera-video:${camerax_version}"   // If you want to additionally use the CameraX View class   implementation "androidx.camera:camera-view:${camerax_version}"   // If you want to additionally use the CameraX Extensions library   implementation "androidx.camera:camera-extensions:${camerax_version}" }

配置选项

可以配置每个 CameraX 用例,以控制用例操作的不同方面。
例如,对于图片拍摄用例,您可以设置目标宽高比和闪光灯模式。
示例:
val imageCapture = ImageCapture.Builder()     .setFlashMode(...)     .setTargetAspectRatio(...)     .build()
除配置选项之外,一些用例会公开 API 以便在创建后动态更改设置。如需了解各个用例的专属配置,请参阅实现预览分析图片图片拍摄

使用模式

// 创建一个CameraXConfig对象,在Application中实现CameraXConfig.Provider接口 class CameraApplication : Application(), CameraXConfig.Provider { // 在getCameraXConfig()中返回CameraXConfig对象 override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setMinimumLoggingLevel(Log.ERROR).build() } }
完成后,将此Application类添加到AndroidManifest.xml中(按照说明 Application  |  Android Developers
?如果您的应用需要在设置 CameraX 配置后了解该配置,请保留 CameraXConfig 对象的本地副本。

摄像头限制器

在第一次调用 ProcessCameraProvider.getInstance() 期间,CameraX 会枚举和查询设备上可用摄像头的特性。由于CameraX需要与硬件通信,因此对每个摄像头执行可能需要较长时间,尤其在低端设备上。
如果App仅使用例如前置摄像头,可以设置CameraX忽略其他摄像头,从而缩短启动延迟时间。
如果传递给 CameraXConfig.Builder.setAvailableCamerasLimiter() 的 CameraSelector  过滤掉了某个摄像头,则 CameraX 在运行时会假定该摄像头不存在。
例如,以下代码会限制应用只能使用设备的默认后置摄像头:
class MainApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .build() } }

线程

构建 CameraX 所基于的很多平台 API 都需要阻止与硬件之间的进程间通信 (IPC),这类通信有时可能需要数百毫秒的响应时间。因此,CameraX 仅从后台线程调用这些 API,这可确保主线程不被阻止,并且界面保持流畅。

摄像头执行器

摄像头执行器用于所有内部摄像头平台 API 调用,以及来自这些 API 的回调。CameraX 会分配和管理内部 Executor 来执行这些任务。

日志记录

CameraX 日志记录让应用可以过滤 logcat 消息,因为最好避免正式版代码中包含详细消息。CameraX 支持四种日志记录级别(从最详细到最严重):
  • Log.DEBUG(默认)
  • Log.INFO
  • Log.WARN
  • Log.ERROR
如需详细了解这些日志级别,请参阅 Android 日志文档。可使用 CameraXConfig.Builder.setMinimumLoggingLevel(int) 为您的应用设置适当的日志记录级别。

自动选择

  • CameraX会根据当前运行App的设备自动提供专用的功能。
  • CameraX 的目标是成功初始化相机会话。这意味着 CameraX 会根据设备功能降低分辨率和宽高比。降低的原因可能是:
    • 设备不支持请求的分辨率。
    • 设备存在兼容性问题,例如需要特定分辨率才能正常运行的旧设备。
    • 在某些设备上,特定格式仅适用于特定宽高比。
    • 对于 JPEG 或视频编码,设备首选“最近的 mod16”。如需了解详情,请参阅 SCALER_STREAM_CONFIGURATION_MAP
  • 尽管 CameraX 会创建并管理会话,但应始终在代码中检查用例输出所返回的图像大小,并进行相应调整。

旋转

默认情况下,在用例创建期间,相机的旋转角度会设置为与默认的显示屏旋转角度保持一致。
  • 通过在配置用例对象时传入当前显示屏方向或在创建用例对象之后动态传入显示屏方向,您可以将旋转角度更改为自定义值以支持多显示屏设备。
  • 您的应用可以使用配置设置来设置目标旋转角度。然后,即使生命周期处于运行状态,应用也可以通过使用用例 API 中的方法(例如 ImageAnalysis.setTargetRotation())更新旋转设置
  • 照片或分析用例需要了解设备当前的旋转角度。
  • 存储所拍摄图片的数据时可能不会包含旋转信息。Exif 数据包含旋转信息,以便图库应用在保存后以正确的屏幕方向显示图片。
如需以正确的屏幕方向显示预览数据,您可以使用 Preview.PreviewOutput() 的元数据输出创建转换。
以下代码示例展示了如何为屏幕方向事件设置旋转角度:
override fun onCreate() { val imageCapture = ImageCapture.Builder().build() val orientationEventListener = object : OrientationEventListener(this as Context) { override fun onOrientationChanged(orientation : Int) { // Monitors orientation values to determine the target rotation value val rotation : Int = when (orientation) { in 45..134 -> Surface.ROTATION_270 in 135..224 -> Surface.ROTATION_180 in 225..314 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageCapture.targetRotation = rotation } } orientationEventListener.enable() }
每个用例都会根据设定的旋转角度直接旋转图片数据或者向用户提供未旋转图片数据的旋转元数据。
  • ImageAnalysis:提供元数据输出,以便了解图片缓冲区坐标相对于显示坐标的位置。
  • ImageCapture:更改图片 Exif 元数据、缓冲区或同时更改两者,从而反映旋转设置。更改的值取决于 HAL 实现

裁剪矩形

默认情况下,剪裁矩形是完整的缓冲区矩形,您可通过 ViewPort 和 UseCaseGroup 对其进行自定义。
通过对用例进行分组并设置视口,CameraX 可保证一个组中的所有用例的剪裁矩形都指向摄像头传感器中的同一个区域。
以下代码段展示了这两个类的使用方法:
val viewPort = ViewPort.Builder(Rational(width, height), display.rotation).build() val useCaseGroup = UseCaseGroup.Builder()     .addUseCase(preview)     .addUseCase(imageAnalysis)     .addUseCase(imageCapture)     .setViewPort(viewPort)     .build() cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)
ViewPort 用于指定最终用户可看到的缓冲区矩形。CameraX 会根据视口的属性及附加的用例计算出可能的最大剪裁矩形。一般情况下,为了达到 WYSIWYG 效果,您应根据预览用例来配置视口。获取视口的一种简单方法是使用 PreviewView
以下代码段展示了如何获取 ViewPort 对象:
val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort
在前面的示例中,应用通过 ImageAnalysis 和 ImageCapture 获取的内容与最终用户在 PreviewView 中看到的内容相同(假定 PreviewView 的缩放类型设为默认值 FILL_CENTER )。将剪裁矩形和旋转角度应用到输出缓冲区后,图片在所有用例中都一样,但分辨率可能会有所不同。如需详细了解如何应用转换信息,请参阅转换输出

选择摄像头

CameraX 会根据应用的要求和用例自动选择最佳摄像头设备。
如果您希望使用的设备与系统为您选择的设备不同,有以下几种选项供您选择:
注意:摄像头设备必须经过系统识别,并显示在 CameraManager.getCameraIdList() 中,然后才可供使用。
以下代码示例展示了如何创建 CameraSelector 来影响选择的设备:
fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? { val cam2Infos = provider.availableCameraInfos.map { Camera2CameraInfo.from(it) }.sortedByDescending { // HARDWARE_LEVEL is Int type, with the order of: // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) } return when { cam2Infos.isNotEmpty() -> { CameraSelector.Builder() .addCameraFilter { it.filter { camInfo -> // cam2Infos[0] is either EXTERNAL or best built-in camera val thisCamId = Camera2CameraInfo.from(camInfo).cameraId thisCamId == cam2Infos[0].cameraId } }.build() } else -> null } } // create a CameraSelector for the USB camera (or highest level internal camera) val selector = selectExternalOrBestCamera(processCameraProvider) processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

相机分辨率

可以选择允许 CameraX 根据设备功能、设备支持的硬件级别、用例和所提供的宽高比组合设置图片分辨率。或者,您也可以在支持相应配置的用例中设置特定目标分辨率或特定宽高比。

自动分辨率

  • CameraX 可以根据 cameraProcessProvider.bindToLifecycle() 中指定的用例自动确定最佳分辨率设置。
  • 图片拍摄和图片分析用例的默认宽高比为 4:3。
  • 用例具有一个可配置的宽高比,让应用可以根据界面设计指定所需的宽高比。
  • CameraX 输出将会生成,以尽可能匹配设备支持的所请求宽高比。如果没有任何支持的完全匹配分辨率,则选择满足最多条件的分辨率。因此,应用决定了相机应如何在应用中显示,CameraX 则会确定最佳相机分辨率设置,以在不同设备上满足这一要求。
例如,应用可以执行以下任一操作:
  • 为用例指定 4:3 或 16:9 的目标分辨率
  • 指定自定义分辨率,CameraX 将尝试查找与其最接近的匹配项
  • 为 ImageCapture 指定剪裁宽高比
CameraX 会自动选择内部 Camera2 界面分辨率。下表显示了这些分辨率:
用例
内部界面分辨率
输出数据分辨率
宽高比:使目标与设置最相符的分辨率。
内部界面分辨率。提供元数据以允许 View 针对目标宽高比进行剪裁、缩放和旋转。
默认分辨率:最高预览分辨率,或与上述宽高比匹配的最高设备首选分辨率。
最大分辨率:预览大小,指的是与设备的屏幕分辨率或 1080p (以较低者为准)匹配的最佳尺寸。
宽高比:使目标与设置最相符的分辨率。
内部界面分辨率。
默认分辨率:默认目标分辨率设置为 640x480。同时调整目标分辨率和相应的宽高比可调出支持的最佳分辨率。
最大分辨率:从 StreamConfigurationMap.getOutputSizes() 中检索到的 YUV_420_888 格式的相机设备最大输出分辨率。目标分辨率默认设置为 640x480;因此,如果您希望分辨率大于 640x480,必须使用 setTargetResolution() 和 setTargetAspectRatio() 从支持的分辨率中选择最接近的一个。
宽高比:最适合设置的宽高比。
内部界面分辨率。
默认分辨率:最高可用分辨率,或与上述宽高比匹配的最高设备首选分辨率。
最大分辨率:JPEG 格式的相机设备最大输出分辨率。请使用 StreamConfigurationMap.getOutputSizes()  检索此分辨率。

指定分辨率

使用 setTargetResolution(Size resolution) 方法构建用例时,可以设置特定分辨率,如以下代码示例所示:
val imageAnalysis = ImageAnalysis.Builder()     .setTargetResolution(Size(1280, 720))     .build()
  • 无法针对同一个用例设置目标宽高比和目标分辨率,这样做会抛出 IllegalArgumentException
  • 目标分辨率会尝试指定图片分辨率的下限,实际的图片分辨率是最接近的可用分辨率,其大小不小于相机实现确定的目标分辨率。
  • CameraX 会根据请求应用最合适的分辨率。
    • 如果主要需求是满足宽高比要求,则仅指定 setTargetAspectRatio,CameraX 会根据设备确定合适的特定分辨率。
    • 如果应用的主要需求是指定分辨率以提高图片处理效率(例如根据设备处理能力处理较小或中等大小的图片),请使用 setTargetResolution(Size resolution)
    • 如果您的应用需要精确的分辨率,请参阅 createCaptureSession() 内的表格,以确定每个硬件级别支持的最大分辨率。如需查看当前设备支持的特定分辨率,请参阅 StreamConfigurationMap.getOutputSizes(int)
注意:如果使用 setTargetResolution(),可能会得到宽高比与其他用例不匹配的缓冲区。如果宽高比必须匹配,请检查两个用例返回的缓冲区尺寸,然后剪裁或缩放其中一个以与另一个匹配。

控制相机输出

CameraX 不仅让您可以视需要为每个单独的用例配置相机输出,还实现了以下接口,从而支持所有绑定用例中常见的相机操作:
  • 利用 CameraInfo,您可以查询这些常用相机功能的状态。
以下是 CameraControl 支持的相机功能:
  • 变焦
  • 手电筒
  • 对焦和测光(点按即可对焦)
  • 曝光补偿

获得CameraControl和CameraInfo的实例

使用 ProcessCameraProvider.bindToLifecyle() 返回的 Camera 对象检索 CameraControl 和 CameraInfo 的实例。
val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // For performing operations that affect all outputs. val cameraControl = camera.cameraControl // For querying information and states. val cameraInfo = camera.cameraInfo
比如说,您可以在调用 bindToLifecycle() 后提交变焦操作及其他 CameraControl 操作。如果您停止或销毁用于绑定相机实例的 Activity,CameraControl 无法再执行操作,并且会返回失败的 ListenableFuture
注意:如果 LifecycleOwner 被停止或销毁,Camera 就会关闭,之后变焦、手电筒、对焦和测光以及曝光补偿控件的所有状态更改均会还原成默认值。

1. 变焦

  • CameraControl提供了两种更改变焦级别的方法
    • setZoomRatio() 用于按变焦比例设置变焦。
      • 该比率必须在 CameraInfo.getZoomState().getValue().getMinZoomRatio() 到 CameraInfo.getZoomState().getValue().getMaxZoomRatio() 的范围内。否则,该函数会返回失败的 ListenableFuture
    • setLinearZoom() 使用 0 到 1.0 之间的线性变焦值设置当前变焦操作。
      • 线性变焦的优势在于,它可以确保视野范围 (FOV) 能够随变焦的变化而缩放。因此线性变焦非常适合与 Slider 视图搭配使用。
  • CameraInfo.getZoomState() 会返回当前变焦状态的 LiveData。在相机初始化时或在使用 setZoomRatio() 或 setLinearZoom() 设置变焦级别的情况下,该值会发生变化。调用任一方法均可设置支持 ZoomState.getZoomRatio() 和 ZoomState.getLinearZoom() 的值。如果您希望在变焦滑块旁边显示变焦比例文字,那么这会很有用。只需观察 ZoomState LiveData 即可更新这两者,而无需进行转换。
  • 这两个 API 返回的 ListenableFuture 让应用可以选择在完成具有指定变焦值的重复请求时收到通知。此外,如果您在上一次变焦操作仍在执行时设置新的变焦值,则上一次变焦操作的 ListenableFuture 会立即失败。

2. 手电筒

  • 可以检查 CameraInfo.hasFlashUnit() 返回的值,确定手电筒功能是否可用。如果手电筒不可用,调用 CameraControl.enableTorch(boolean) 会使返回的 ListenableFuture 立即完成,显示失败的结果,并将手电筒的状态设置为 TorchState.OFF
  • 启用手电筒后,无论闪光灯模式设置如何,手电筒在拍照和拍视频过程中会保持开启状态。仅当手电筒被停用时,ImageCapture 中的 flashMode 才会起作用。

3. 对焦和测光

CameraControl.startFocusAndMetering() 可根据指定的 FocusMeteringAction 设置 AF/AE/AWB 测光区域,以触发自动对焦和曝光测光。有许多相机应用通过这种方式实现“点按即可对焦”功能。
MeteringPoint
首先,使用 MeteringPointFactory.createPoint(float x, float y, float size) 创建 MeteringPoint。(MeteringPoint 表示相机 Surface 上的单个点。它以标准化形式存储,所以能轻松转换为传感器坐标,从而用于指定 AF/AE/AWB 区域。)
MeteringPoint 的大小介于 0 到 1 之间,默认大小为 0.15f。调用 MeteringPointFactory.createPoint(float x, float y, float size) 时,CameraX 会为提供的 size 创建以 (x, y) 为中心的矩形区域。
创建:
// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview. previewView.setOnTouchListener((view, motionEvent) -> { val meteringPoint = previewView.meteringPointFactory .createPoint(motionEvent.x, motionEvent.y) … } // Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for // preview. Please note that if the preview is scaled or cropped in the View, // it’s the application's responsibility to transform the coordinates properly // so that the width and height of this factory represents the full Preview FOV. // And the (x,y) passed to create MeteringPoint may need to be adjusted with // the offsets. val meteringPointFactory = DisplayOrientedMeteringPointFactory( surfaceView.display, camera.cameraInfo, surfaceView.width, surfaceView.height ) // Use SurfaceOrientedMeteringPointFactory if the point is specified in // ImageAnalysis ImageProxy. val meteringPointFactory = SurfaceOrientedMeteringPointFactory( imageWidth, imageHeight, imageAnalysis)
startFocusAndMetering 和 FocusMeteringAction
如需调用 startFocusAndMetering(),应用必须构建 FocusMeteringAction,其中包含一个或多个 MeteringPoints,后者由 FLAG_AFFLAG_AEFLAG_AWB 这些可选测光模式组合而成。
下面的代码演示了这一用法:
val meteringPoint1 = meteringPointFactory.createPoint(x1, x1) val meteringPoint2 = meteringPointFactory.createPoint(x2, y2) // startFocusAndMetering() 所用的 FocusMeteringAction 包含一个 AF/AE/AWB 测光区域的 MeteringPoint, // 还有一个 AF 和 AE 的 MeteringPoint。 // 在内部,CameraX 会将其转换为 Camera2 MeteringRectangles, // 并将相应的 CONTROL_AF_REGIONS/CONTROL_AE_REGIONS/CONTROL_AWB_REGIONS 参数设置为拍摄请求。 val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB // Optionally add meteringPoint2 for AF/AE. .addPoint(meteringPoint2, FLAG_AF | FLAG_AE) // The action will be canceled in 3 seconds (if not set, default is 5s). .setAutoCancelDuration(3, TimeUnit.SECONDS) .build() val result = cameraControl.startFocusAndMetering(action) // Adds listener to the ListenableFuture if you need to know the focusMetering result. result.addListener({ // result.get().isFocusSuccessful returns if the auto focus is successful or not. }, ContextCompat.getMainExecutor(this)
由于并非所有设备都支持 AF/AE/AWB 和多个区域,CameraX 会尽最大努力执行 FocusMeteringAction。CameraX 会使用所支持的最大数量的 MeteringPoints,并且按测光点的添加顺序依次使用。对于在超出支持的最大数量之外添加的所有 MeteringPoints,CameraX 会一律忽略。
曝光补偿
当应用需要对自动曝光 (AE) 输出结果以外的曝光值 (EV) 进行微调时,曝光补偿很有用。CameraX 将按以下方式组合曝光补偿值,以确定当前图像条件下所需的曝光:
Exposure = ExposureCompensationIndex * ExposureCompensationStep
CameraX 提供 Camera.CameraControl.setExposureCompensationIndex() 函数,用于将曝光补偿设置为索引值:当索引值为正值时,会调亮图片;当索引值为负值时,会调暗图片。
应用可以按下一部分中所述的 CameraInfo.ExposureState.exposureCompensationRange() 查询支持的范围。如果相应的值受支持,则当在拍摄请求中成功启用该值时,返回的 ListenableFuture 便会完成;如果指定的索引超出支持范围,则 setExposureCompensationIndex() 会导致返回的 ListenableFuture 立即完成,并显示失败的结果。
CameraX 仅保留最新的未完成 setExposureCompensationIndex() 请求。如果在上一个请求尚未执行时便多次调用该函数,会导致取消请求。
下面的代码段设置了曝光补偿索引,并注册一个回调,以便知晓曝光更改请求何时被执行:
camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex) .addListener({ // Get the current exposure compensation index, it may be // different from the asked value in case this request was // canceled by a newer setting request. val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex … }, mainExecutor)
例如,下面的代码会使用当前的 ExposureState 值初始化曝光 SeekBar 的设置:
val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }

预览

使用 PreviewView,这是一种可以剪裁、缩放和旋转以确保正确显示的 View。当相机处于活动状态时,图片预览会流式传输到 PreviewView 中的 Surface。

使用PreviewView

  • 步骤:
  • 使用PreviewView时无法执行以下任何操作,若执行,则就会停止帧流式传输:
    • 创建 SurfaceTexture,以在 TextureView 和 Preview.SurfaceProvider 上进行设置。
    • 从 TextureView 检索 SurfaceTexture,并在 Preview.SurfaceProvider 上对其进行设置。
    • 从 SurfaceView 获取 Surface,并在 Preview.SurfaceProvider 上对其进行设置。
  1. 将PreviewView添加到布局
<FrameLayout android:id="@+id/container"> <androidx.camera.view.PreviewView android:id="@+id/previewView" /> </FrameLayout>
  1. 请求CameraProvider
import androidx.camera.lifecycle.ProcessCameraProvider import com.google.common.util.concurrent.ListenableFuture class MainActivity : AppCompatActivity() { private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider> override fun onCreate(savedInstanceState: Bundle?) { cameraProviderFuture = ProcessCameraProvider.getInstance(this) } }
  1. 检查CameraProvider可用性
// 请求 CameraProvider 后,请验证它能否在视图创建后成功初始化。 cameraProviderFuture.addListener(Runnable { val cameraProvider = cameraProviderFuture.get() bindPreview(cameraProvider) }, ContextCompat.getMainExecutor(this))
  1. 选择相机并绑定生命周期和用例
    1. 在创建并确认CameraProvider后执行:
    2. 创建 Preview
    3. 指定所需的相机 LensFacing 选项。
    4. 将所选相机和任意用例绑定到生命周期。
    5. 将 Preview 连接到 PreviewView
    6. fun bindPreview(cameraProvider : ProcessCameraProvider) {     var preview : Preview = Preview.Builder()             .build()     var cameraSelector : CameraSelector = CameraSelector.Builder()           .requireLensFacing(CameraSelector.LENS_FACING_BACK)           .build()     preview.setSurfaceProvider(previewView.getSurfaceProvider())     var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview) }
badge