1. CameraX 预览与拍照最小实现
CameraX 预览与拍照最小实现
CameraX 通过“用例(UseCase)”把相机能力拆成可组合的模块。本例只绑定两个用例:Preview 负责把相机画面送到 PreviewView,ImageCapture 负责把一帧画面编码成 JPEG 文件。两者共享同一个 ProcessCameraProvider 和 CameraSelector,由 CameraX 统一管理相机打开、会话配置与生命周期。
关键约束:ImageCapture 的输出目录必须由应用自己提供。本例写入 getExternalFilesDir(Environment.DIRECTORY_PICTURES),即应用私有外部目录,无需存储权限,卸载应用时随应用数据一起清除。
最小实现代码
以下代码可直接放入一个 Activity 中运行。布局只需一个 PreviewView 和一个按钮。
// build.gradle (Module: app)
// implementation "androidx.camera:camera-core:1.3.4"
// implementation "androidx.camera:camera-camera2:1.3.4"
// implementation "androidx.camera:camera-lifecycle:1.3.4"
// implementation "androidx.camera:camera-view:1.3.4"
class CameraActivity : AppCompatActivity() {
private lateinit var previewView: PreviewView
private lateinit var imageCapture: ImageCapture
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
previewView = findViewById(R.id.previewView)
cameraExecutor = Executors.newSingleThreadExecutor()
findViewById<Button>(R.id.btnCapture).setOnClickListener {
takePhoto()
}
startCamera()
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture
)
} catch (e: Exception) {
Log.e("CameraX", "绑定失败", e)
}
}, ContextCompat.getMainExecutor(this))
}
private fun takePhoto() {
val name = "IMG_${SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())}.jpg"
val photoFile = File(
getExternalFilesDir(Environment.DIRECTORY_PICTURES), name
)
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
Log.d("CameraX", "已保存: ${photoFile.absolutePath}")
}
override fun onError(exc: ImageCaptureException) {
Log.e("CameraX", "拍照失败: ${exc.message}", exc)
}
}
)
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
}
验证方式:运行后预览画面应实时显示后置摄像头画面;点击按钮后 Logcat 输出
已保存: /storage/emulated/0/Android/data/<包名>/files/Pictures/IMG_xxx.jpg。用 Android Studio 的 Device File Explorer 进入该路径可看到 JPEG 文件,打开后画面与拍照瞬间的预览一致。
常见问题:若预览黑屏,检查是否在
AndroidManifest.xml 中声明了 <uses-permission android:name="android.permission.CAMERA"/> 并在运行时请求了该权限。CameraX 不会自动申请权限,缺少权限时 bindToLifecycle 会抛出异常。
关键参数与行为对照
| 配置项 | 取值 | 作用 |
|---|---|---|
| CameraSelector | DEFAULT_BACK_CAMERA | 选择后置摄像头 |
| CaptureMode | MINIMIZE_LATENCY | 优先降低快门延迟,适合抓拍 |
| 输出目录 | getExternalFilesDir(PICTURES) | 应用私有目录,免存储权限 |
| 回调线程 | ContextCompat.getMainExecutor(this) | 在主线程更新 UI 或日志 |
| 生命周期绑定 | bindToLifecycle(this, ...) | 随 Activity 自动开关相机 |