Android CameraX 相机库
kotlin dependencies { val camerax_version = "1.4.1" // 核心 CameraX 库 implementation("androidx.camera:camera-core:${camerax_version}") // CameraX 生命周期感知组件 implementation("androidx.camera:camera-lifecycle:${camerax_version}") // 用于显示预览的 View implementation("androidx.camera:camera-view:${camerax_version}") // 可选:如果你想使用 Camera2 的互操作功能 implementation("androidx.camera:camera-camera2:${camerax_version}") }
> **注意**:务必使用相同版本的 CameraX 依赖,避免版本冲突。
---
## 权限处理
在 `AndroidManifest.xml` 中声明相机权限:
```xml
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
如果是拍照并保存到外部存储,还需要存储写入权限(Android 10 以下):
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
在 Activity 中动态请求权限:
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
startCamera()
} else {
// 处理权限被拒情况
}
}
private fun checkCameraPermission() {
when {
ContextCompat.checkSelfPermission(
this, Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED -> startCamera()
else -> requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
核心用例详解
CameraX 将相机操作抽象为三个基本用例,你可以根据需要组合使用。
1. Preview(预览)
将取景流显示在界面上。使用 PreviewView 来承载画面。
<!-- layout/activity_main.xml -->
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
2. ImageCapture(拍照)
拍摄照片并保存到文件或获取 Bitmap。在绑定用例时构建。
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) // 或 MAXIMIZE_QUALITY
.build()
// 拍照并保存到文件
fun takePhoto() {
val imageFile = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(imageFile).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
// 照片已保存到 imageFile
}
override fun onError(exception: ImageCaptureException) {
// 处理错误
}
}
)
}
3. ImageAnalysis(帧分析)
对相机帧进行实时处理,如二维码扫描、图像识别等。
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this)) { imageProxy ->
// 在这里处理每一帧,比如传入 ML Kit
val mediaImage = imageProxy.image
if (mediaImage != null) {
// 使用 mediaImage 进行分析
}
// 用完记得关闭
imageProxy.close()
}
绑定生命周期并启动相机
用 ProcessCameraProvider 将用例绑定到当前生命周期的拥有者。
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// 选择后置摄像头作为默认
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 解绑所有用例再重新绑定
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this, // LifecycleOwner
cameraSelector,
preview,
imageCapture,
imageAnalysis
)
} catch (e: Exception) {
// 处理异常
}
}, ContextCompat.getMainExecutor(this))
}
绑定成功后,预览就会自动显示在 PreviewView 中。
高级配置
选择镜头与摄像头
通过 CameraSelector 可以精确选择:
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_FRONT) // 前置摄像头
.build()
如果设备没有对应镜头,CameraX 会自动降级到可用的镜头。
分辨率策略
设置目标分辨率和宽高比:
val preview = Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setTargetResolution(android.util.Size(1920, 1080))
.build()
对于 ImageCapture,可以设置目标分辨率,但最终由设备实际支持的分辨率决定,CameraX 会选择最接近的。
启用闪光灯、旋转等
// 在 Preview 或 ImageCapture 中设置
val camera = cameraProvider.bindToLifecycle(...)
// 控制闪光灯
camera.cameraControl.enableTorch(true) // 打开手电筒
// 手动设置曝光补偿等(需要 Camera2 互操作)
与其他库集成
- 二维码扫描:结合 ML Kit Barcode Scanning,在
ImageAnalysis中处理。 - Camera2 互操作:通过
Camera2Interop.Extender访问底层 Camera2 特性。
完整示例代码片段
下面是一个在 Fragment 中使用 CameraX 的最小化完整示例(省略了布局文件,仅展示 Kotlin 代码):
class CameraFragment : Fragment() {
private lateinit var viewFinder: PreviewView
private lateinit var imageCapture: ImageCapture
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_camera, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewFinder = view.findViewById(R.id.viewFinder)
checkCameraPermission()
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
imageCapture = ImageCapture.Builder().build()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
viewLifecycleOwner, cameraSelector, preview, imageCapture
)
} catch (e: Exception) {
Log.e("CameraX", "绑定失败", e)
}
}, ContextCompat.getMainExecutor(requireContext()))
}
private fun takePhoto() {
val photoFile = File(requireContext().externalMediaDirs.first(), "photo.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(requireContext()),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
Toast.makeText(requireContext(), "照片已保存", Toast.LENGTH_SHORT).show()
}
override fun onError(exception: ImageCaptureException) {
Toast.makeText(requireContext(), "拍照失败", Toast.LENGTH_SHORT).show()
}
}
)
}
// 省略 checkCameraPermission 方法,见上文
}