一、概念
CameraX 解决了由于碎片化问题在不同机型上保持一致性体验的麻烦, 只需要关心 API 调用,不需要对不同机型做适配。CameraX 把不同的功能,如 预览、拍照等功能封装成不同的 UseCase,这样就可以根据业务的需要,来绑定不同的 UseCase 来实现具体的功能。Camera2 库是更底层开发用到的,它的上一代 Camera 库已废弃。
| PreviewView 控件 | 一种可以剪裁、缩放和旋转以确保正确显示的 View,当相机处于活动状态时,图片预览会流式传输到 PreviewView 中的 Surface。 | |
| CameraSelector | 选择或切换镜头(切换需要重新绑定)。 | |
| UserCase | Preview 预览 | 接收用于预览的 Surface,如 PreviewView。 |
| ImageCapture 图片拍摄 | 拍摄并保存图片,可以设置大小、曝光度。 | |
| VideoCapture 视频拍摄 | 拍摄视频和音频。 | |
| ImageAnalysis 分析 | 提供可访问的缓冲区,用来图像识别(人脸、二维码)、机器学习。 | |
1.1 需求划分
| Intent 基本拍摄 | 使用 Intent 方式跳转自带相机来拍照/录像,详见Activity Result API。 |
| CameraController 简单使用 | 在单个类中提供大多数 CameraX 核心功能。它只需少量设置代码,并且可自动处理相机初始化、用例管理、目标旋转、点按对焦、双指张合缩放等操作。扩展 CameraController 的具体类为 LifecycleCameraController。 |
| CameraProvider 进阶使用 |
Intent 代码举例
class MainActivity : AppCompatActivity() {
val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap ->
//TODO...
}
override fun onCreate(savedInstanceState: Bundle?) {
takePictureLauncher.launch(null)
}
}
CameraController 代码举例
private fun initCameraX() {
previewView = binding.previewView
cameraController = LifecycleCameraController(this)
cameraController.bindToLifecycle(this)
cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
previewView.controller = cameraController
//其它可配置项详见 CameraProvider 的 ImageCapture,举例2个
cameraController.imageCaptureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY
cameraController.imageCaptureFlashMode = ImageCapture.FLASH_MODE_AUTO
}
fun takePhoto() {
//切换模式
cameraController.setEnabledUseCases(CameraController.IMAGE_CAPTURE or CameraController.IMAGE_ANALYSIS)
//后续API调用步骤详见 CameraController 的 ImageCapture,一样的。
}
fun startRecording() {
//切换模式
cameraController.setEnabledUseCases(CameraController.VIDEO_CAPTURE)
}
二、初始化
2.1 添加依赖
def camerax_version = "1.4.0-beta02"
implementation "androidx.camera:camera-camera2:$camerax_version"
//避免手动在生命周期释放和销毁数据
implementation "androidx.camera:camera-lifecycle:$camerax_version"
//用里面的PreviewView,它会自行判断用SurfaceView还是TextureView来实现
implementation "androidx.camera:camera-view:$camerax_version"
//可实现人像、HDR、夜间和美颜、滤镜但依赖于OEM
implementation "androidx.camera:camera-extensions:$camerax_version"
2.2 权限声明
//any可以使用设备上的任何一个摄像头,确保设备配有相机。
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
3.3 日志打印(可选配置)
class APP : Application(), CameraXConfig.Provider {
override fun getCameraXConfig(): CameraXConfig {
return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setMinimumLoggingLevel(Log.ERROR)
.build()
}
}
三、预览 Preview
- 将 PreviewView 添加到布局中。
- 获取 CameraProvider 并检查可用性。
- 创建 CameraSelector 选择镜头。
- 创建 Preview 用例并连接到 PreviewView。
- 绑定用例和生命周期。
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
private fun startCamera() {
//获取实例
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
//添加监听器,摄像头准备好后执行(监听CameraX初始化完成)
//传入一个Runnable和一个主线程运行的Executor
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get() //用来绑定生命周期
//设置CameraSelector(或直接赋值CameraSelector.DEFAULT_BACK_CAMERA)
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
//设置Preview用例
val preview = Preview.Builder().build().apply {
surfaceProvider = previewView.surfaceProvider //设置预览的控件,previewView是xml中的PreviewView控件的id
}
try {
//重新绑定前先解绑所有用例
//取消当前已经绑定的摄像头设备,释放它们的资源,以便其他应用或者进程可以使用这些摄像头设备。
//这个方法通常在摄像头应用程序退出或者暂停时调用,以确保摄像头设备不会一直占用系统资源。
cameraProvider.unbindAll()
//与当前生命周期绑定,以便在应用程序暂停或停止时自动释放相机资源
//传入 LifecycleOwner、CameraSelector、以及N个用例(UserCase)
//返回一个Camera对象,进而可以获取CameraInfo(用来查询信息状态)和CameraControl(用来对所有输出结果产生统一影响的操作)对象
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
} catch (e: Exception) {
Log.e("CameraX", "绑定用例失败", e)
}
}, mainExecutor) //或写成ContextCompat.getMainExecutor(this)
}
3.1 帧率
| setTargetFrameRate() | public Builder setTargetFrameRate(Range<Integer> targetFrameRate) 取值范围:30-60FPS。 |
val preview = Preview.Builder()
.setTargetFrameRate(Range(30, 60)) //帧率范围:30-60 FPS
.build()
3.2 旋转方向
默认情况下,摄像头的旋转角度会设置为与显示屏旋转角度保持一致,来确保生成的输出与预览中看到的一致。
| setTargetRotation() | public Builder setTargetRotation(int rotation) CameraX 会自动处理设备旋转,也可以手动调整。 |
监听方式一:旋转后会崩溃报错调用方法的对象为null。20251016
val preview = Preview.Builder()
.setTargetRotation(previewView.display.rotation) // 根据屏幕方向设置旋转
.build()
//在 Activity 中监听设备旋转并更新预览方向
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updatePreviewRotation()
}
private fun updatePreviewRotation() {
val rotation = previewView.display.rotation
preview.targetRotation = rotation
}
监听方式二
override fun onCreate(savedInstanceState: Bundle?) {
val orientationEventListener = object : OrientationEventListener(this) {
override fun onOrientationChanged(orientation : Int) {
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
preview.targetRotation = rotation
}
}
orientationEventListener.enable() //记得在onDestroy()中关闭
}
3.3 宽高比(已过时)
| setTargetAspectRatio() | public Builder setTargetAspectRatio(int aspectRatio) RATIO_DEFAULT:自适应(默认) RATIO_16_9:十六比九。 RATIO_4_3:四比三。 |
val preview = Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_16_9) // 设置 16:9 宽高比
.build()
3.4 预览分辨率(已过时)
| setTargetResolution() | public Builder setTargetResolution(Size resolution) CameraX 会自动选择最接近的分辨率,,也可以手动设置。 |
val preview = Preview.Builder()
.setTargetResolution(Size(1280, 720)) //预览分辨率
.build()
四、拍照 ImageCapture
- 创建 ImageCapture 用例,抽取成全局变量并绑定用例。
- 创建 OutputFileOptions,指定文件存储路径(MediaStore、File、OutputStream)。
- 调用 takePicture() 函数并处理回调。
//抽取成成员变量
private var imageCapture: ImageCapture? = null
//同Preview一样在cameraProviderFuture中添加用例并绑定到生命周期
private fun startCamera() {
imageCapture = ImageCapture.Builder().build()
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
}
private fun takePhoto() {
val imageCapture = imageCapture ?: return
//存储路径和参数
val time = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(System.currentTimeMillis())
val fileName = "CameraX_${time}" //这里不用加格式后缀
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") //指定文件格式会自动加后缀
//Android10以后可以指定相对目录(否则默认保存在Picture根目录)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.RELATIVE_PATH, "${Environment.DIRECTORY_PICTURES}/CameraX")
}
}
//保存在 MediaStore 中,以便其他应用可以显示它
//重载还可以使用 File 或 OutputStream 的方式输出
val outputOptions = ImageCapture.OutputFileOptions.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
).build()
//拍照后的回调函数
imageCapture.takePicture(outputOptions, mainExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
//可以拿到Uri
val uri = outputFileResults.savedUri
}
override fun onError(exception: ImageCaptureException) {
TODO("Not yet implemented")
}
})
}
//举例使用File保存,但不会被添加到媒体数据库中,需使用 MediaScannerConnection.scanFile()
val file = File(getExternalFilesDir(Environment.DIRECTORY_DCIM), fileName)
val outputOptions = ImageCapture.OutputFileOptions.Builder(file).build()
4.1 拍摄模式
| setCaptureMode() | public Builder setCaptureMode(int captureMode) CAPTURE_MODE_MINIMIZE_LATENCY:优先降低拍照延迟。 CAPTURE_MODE_MAXIMIZE_QUALITY:优先提高照片质量。 CAPTURE_MODE_ZERO_SHUTTER_LAG:更低延迟。 |
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
4.2 闪光灯模式
| setFlashMode() | public Builder setFlashMode(int flashMode) FLASH_MODE_OFF:关闭闪光灯。 FLASH_MODE_ON:开启闪光灯。 FLASH_MODE_AUTO:根据环境光线自动决定是否开启闪光灯。 |
val imageCapture = ImageCapture.Builder()
.setFlashMode(ImageCapture.FLASH_MODE_AUTO)
.build()
//也可以动态修改
imageCapture.flashMode = ImageCapture.FLASH_MODE_ON
4.3 输出分辨率(已过时)
| setTargetResolution() | public Builder setTargetResolution(Size resolutio) 可以控制输出照片的尺寸。 |
4.4 宽高比(已过时)
| setTargetAspectRatio() | public Builder setTargetAspectRatio(int aspectRatio) 不同设备的摄像头支持的宽高比可能不同,使用 RATIO_DEFAULT 可以自动选择最适合当前设备的宽高比。 RATIO_DEFAULT:自适应(默认) RATIO_16_9:十六比九。 RATIO_4_3:四比三。 |
val imageCapture = ImageCapture.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_DEFAULT)
.build()
//获取设备默认宽高比
cameraProviderFuture.addListener({
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
val cameraInfo = camera.cameraInfo
val supportedAspectRatios = cameraInfo.supportedAspectRatios
if (supportedAspectRatios.contains(AspectRatio.RATIO_4_3)) {
Log.d("CameraX", "设备支持 4:3 宽高比")
}
if (supportedAspectRatios.contains(AspectRatio.RATIO_16_9)) {
Log.d("CameraX", "设备支持 16:9 宽高比")
}
}, ContextCompat.getMainExecutor(this))
4.5 旋转方向
| setTargetRotation() | public Builder setTargetRotation(int rotation) |
val imageCapture = ImageCapture.Builder()
.setTargetRotation(previewView.display.rotation) // 根据屏幕方向设置旋转
.build()
4.6 JEPG质量
| setJpegQuality() | public Builder setJpegQuality(int jpegQuality) 取值范围:1-100。 |
val imageCapture = ImageCapture.Builder()
.setJpegQuality(90) //取值范围1-100
.build()
五、录像 VideoCapture
| Recoder | 是与 VideoCapture 紧密耦合的 VideoOutput 实现。 Recorder 用于执行视频和音频捕获操作。应用通过 Recorder 创建录制对象。 |
| PendingRecording | 会配置录制对象,同时提供启用音频和设置事件监听器等选项。您必须使用 Recorder 来创建 PendingRecording。PendingRecording 不会录制任何内容。 |
| Recording | 会执行实际录制操作。您必须使用 PendingRecording 来创建 Recording。 |
//抽取成成员变量
private var videoCapture: VideoCapture<Recorder>? = null
//同Preview一样在cameraProviderFuture中添加用例并绑定到生命周期
private fun startCamera() {
val recorder = Recorder.Builder()
.setQualitySelector(QualitySelector.from(Quality.FHD))
.build()
videoCapture = VideoCapture.withOutput(recorder)
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, videoCapture)
}
//抽取成成员变量
private var recoding: Recording? = null
fun recoding(context: Context) {
val videoCapture = videoCapture ?: return
//正在录制就停止
recoding?.let {
it.stop()
recoding = null
}
val contentResolver = context.contentResolver
val time = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(System.currentTimeMillis())
val fileName = "CameraX_${time}"
val contentValues = ContentValues().apply {
put(MediaStore.Video.Media.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
}
val outputOptions = MediaStoreOutputOptions.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
).setContentValues(contentValues).build()
recoding = videoCapture.output
.prepareRecording(context, outputOptions)
.withAudioEnabled()
.start(context.mainExecutor) { event ->
}
}
六、分析 ImageAnalysis
七、Camera
CameraProvider 绑定生命周期时会返回一个 Camera 对象,进而获取 CameraInfo 和 CameraControl 对象。
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
val cameraInfo = camera.cameraInfo
val cameraControl = camera.cameraControl
7.1 曝光补偿
| setExposureCompensationIndex() | setExposureCompensationIndex(int value) 控制照片的亮度,值通常是一个范围。通过 CameraInfo 获取。 |
val range = cameraInfo.exposureState.exposureCompensationRange
if (range.contains(1)) {
cameraControl.setExposureCompensationIndex(1) // 设置曝光补偿值
}
7.2 预览的缩放比
| setZoomRatio() | ListenableFuture<Void> setZoomRatio(float ratio) 设置预览画面的缩放比例,以放大或缩小预览内容。 |
cameraControl.setZoomRatio(2.0f) // 设置缩放比例为 2.0
八、PreviewView
8.1 缩放
| setScaleType() | 提供了几种缩放类型,用于控制预览画面的显示方式: FILL_CENTER:填充整个视图,可能会裁剪部分画面。 FIT_START:保持宽高比,可能会在顶部或左侧留出空白。 FIT_CENTER:保持宽高比,可能会在上下或左右留出空白。 FIT_END:保持宽高比,可能会在底部或右侧留出空白。 |
previewView.scaleType = PreviewView.ScaleType.FIT_CENTER
1228

被折叠的 条评论
为什么被折叠?



