向后兼容至Android 5.0
PreviewView
。VideoCapture
拍摄视频和音频takePicture()
方法调用。cameraProvider.bindToLifecycle()
指定要与相机关联的生命周期。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),则必须通过使用 ProcessCameraProvider.unbindAll()
或分别取消绑定各个用例来确保所有用例均未绑定 CameraX。或者,当您将用例绑定到生命周期时,可以让 CameraX 管理拍摄会话的开启和关闭操作以及用例的取消绑定操作。
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)) }
ImageCapture
和 Preview
的组合。根据 OEM 实现情况,可能无法同时添加 ImageAnalysis
;无法为 VideoCapture
用例启用扩展。如需了解详情,请参阅扩展参考文档。@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
权限。WRITE_EXTERNAL_STORAGE
权限。FragmentActivity
或 AppCompatActivity
。// 项目的settings.gradle dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() // 添加google()代码库 mavenCentral() } }
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // For Kotlin projects kotlinOptions { jvmTarget = "1.8" } }
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}" }
CameraXConfig
接口实现此目的。CameraXConfig
,应用可以执行以下操作:setAvailableCameraLimiter()
优化启动延迟时间。setCameraExecutor()
向 CameraX 提供应用执行器。setSchedulerHandler()
。setMinimumLoggingLevel()
更改日志记录级别。// 创建一个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() } }
?如果您的应用需要在设置 CameraX 配置后了解该配置,请保留 CameraXConfig
对象的本地副本。
ProcessCameraProvider.getInstance()
期间,CameraX 会枚举和查询设备上可用摄像头的特性。由于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() } }
摄像头执行器用于所有内部摄像头平台 API 调用,以及来自这些 API 的回调。CameraX 会分配和管理内部 Executor
来执行这些任务。
Log.DEBUG
(默认)Log.INFO
Log.WARN
Log.ERROR
CameraXConfig.Builder.setMinimumLoggingLevel(int)
为您的应用设置适当的日志记录级别。SCALER_STREAM_CONFIGURATION_MAP
。ImageAnalysis.setTargetRotation()
)更新旋转设置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() }
Preview.getTargetRotation()
了解目标分辨率的旋转设置。ViewPort
和 UseCaseGroup
对其进行自定义。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
)。将剪裁矩形和旋转角度应用到输出缓冲区后,图片在所有用例中都一样,但分辨率可能会有所不同。如需详细了解如何应用转换信息,请参阅转换输出。CameraSelector.DEFAULT_FRONT_CAMERA
请求默认的前置摄像头。CameraSelector.DEFAULT_BACK_CAMERA
请求默认的后置摄像头。CameraSelector.Builder.addCameraFilter()
按 CameraCharacteristics
过滤可用设备的列表。注意:摄像头设备必须经过系统识别,并显示在 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)
cameraProcessProvider.bindToLifecycle()
中指定的用例自动确定最佳分辨率设置。ImageCapture
指定剪裁宽高比setTargetResolution(Size resolution)
方法构建用例时,可以设置特定分辨率,如以下代码示例所示:val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
IllegalArgumentException
setTargetAspectRatio
,CameraX 会根据设备确定合适的特定分辨率。setTargetResolution(Size resolution)
。createCaptureSession()
内的表格,以确定每个硬件级别支持的最大分辨率。如需查看当前设备支持的特定分辨率,请参阅 StreamConfigurationMap.getOutputSizes(int)
。注意:如果使用 setTargetResolution()
,可能会得到宽高比与其他用例不匹配的缓冲区。如果宽高比必须匹配,请检查两个用例返回的缓冲区尺寸,然后剪裁或缩放其中一个以与另一个匹配。
isSessionConfigurationSupported()
验证特定的 SessionConfiguration
。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
就会关闭,之后变焦、手电筒、对焦和测光以及曝光补偿控件的所有状态更改均会还原成默认值。
setZoomRatio()
用于按变焦比例设置变焦。CameraInfo.getZoomState().getValue().getMinZoomRatio()
到 CameraInfo.getZoomState().getValue().getMaxZoomRatio()
的范围内。否则,该函数会返回失败的 ListenableFuture
。setLinearZoom()
使用 0 到 1.0 之间的线性变焦值设置当前变焦操作。Slider
视图搭配使用。CameraInfo.getZoomState()
会返回当前变焦状态的 LiveData。在相机初始化时或在使用 setZoomRatio()
或 setLinearZoom()
设置变焦级别的情况下,该值会发生变化。调用任一方法均可设置支持 ZoomState.getZoomRatio()
和 ZoomState.getLinearZoom()
的值。如果您希望在变焦滑块旁边显示变焦比例文字,那么这会很有用。只需观察 ZoomState
LiveData
即可更新这两者,而无需进行转换。ListenableFuture
让应用可以选择在完成具有指定变焦值的重复请求时收到通知。此外,如果您在上一次变焦操作仍在执行时设置新的变焦值,则上一次变焦操作的 ListenableFuture
会立即失败。CameraControl.enableTorch(boolean)
可以启用或停用手电筒。CameraInfo.getTorchState()
可用于查询当前的手电筒状态。CameraInfo.hasFlashUnit()
返回的值,确定手电筒功能是否可用。如果手电筒不可用,调用 CameraControl.enableTorch(boolean)
会使返回的 ListenableFuture
立即完成,显示失败的结果,并将手电筒的状态设置为 TorchState.OFF
。ImageCapture
中的 flashMode
才会起作用。CameraControl.startFocusAndMetering()
可根据指定的 FocusMeteringAction 设置 AF/AE/AWB 测光区域,以触发自动对焦和曝光测光。有许多相机应用通过这种方式实现“点按即可对焦”功能。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
,其中包含一个或多个 MeteringPoints
,后者由 FLAG_AF
、FLAG_AE
、FLAG_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)
FocusMeteringAction
。CameraX 会使用所支持的最大数量的 MeteringPoints,并且按测光点的添加顺序依次使用。对于在超出支持的最大数量之外添加的所有 MeteringPoints,CameraX 会一律忽略。Exposure = ExposureCompensationIndex * ExposureCompensationStep
Camera.CameraControl.setExposureCompensationIndex()
函数,用于将曝光补偿设置为索引值:当索引值为正值时,会调亮图片;当索引值为负值时,会调暗图片。CameraInfo.ExposureState.exposureCompensationRange()
查询支持的范围。如果相应的值受支持,则当在拍摄请求中成功启用该值时,返回的 ListenableFuture
便会完成;如果指定的索引超出支持范围,则 setExposureCompensationIndex()
会导致返回的 ListenableFuture
立即完成,并显示失败的结果。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)
Camera.CameraInfo.getExposureState()
可检索当前的 ExposureState
,其中包括:ExposureState
值初始化曝光 SeekBar
的设置:val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }
CameraXConfig.Provider
。PreviewView
添加到布局。View
时,请检查 ProcessCameraProvider
。SurfaceTexture
,以在 TextureView
和 Preview.SurfaceProvider
上进行设置。TextureView
检索 SurfaceTexture
,并在 Preview.SurfaceProvider
上对其进行设置。SurfaceView
获取 Surface
,并在 Preview.SurfaceProvider
上对其进行设置。<FrameLayout android:id="@+id/container"> <androidx.camera.view.PreviewView android:id="@+id/previewView" /> </FrameLayout>
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) } }
// 请求 CameraProvider 后,请验证它能否在视图创建后成功初始化。 cameraProviderFuture.addListener(Runnable { val cameraProvider = cameraProviderFuture.get() bindPreview(cameraProvider) }, ContextCompat.getMainExecutor(this))
Preview
。LensFacing
选项。Preview
连接到 PreviewView
。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) }