本文着重介绍 CameraX 的使用
CameraX 是 Jatpack 组件中的一个,基于 Camera2 做的再封装,使用较 Camera2 简单,性能较 Camera1 好
1、引入 CameraX 库
[versions]
camerax = "1.3.0"
[libraries]
camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "camerax" }
camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "camerax" }
camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "camerax" }
dependencies {
implementation libs.camera.camera2
implementation libs.camera.lifecycle
implementation libs.camera.view
}
2、打开摄像头,并渲染摄像头画面
要使用 PreviewView 布局对象
<androidx.camera.view.PreviewView
android:id="@+id/camera_preview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
打开摄像头逻辑 ImageCapture 对象会持续收集摄像头数据
lateinit var imageCapture: ImageCapture
fun startCamera() {
var cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
try {
// 将相机的生命周期和 activity 生命周期绑定
val cameraProvider = cameraProviderFuture.get()
// 预览 capture,支持角度转换
val preview = Preview.Builder()
.setTargetRotation(currentRotation)
.build().also {
it.setSurfaceProvider(camera_preview.surfaceProvider)
}
// 创建图片的 capture
imageCapture = ImageCapture.Builder()
.setTargetRotation(camera_preview.display.rotation)
.setFlashMode(ImageCapture.FLASH_MODE_AUTO)
.build()
// 选择后置摄像头
val cameraSlactor = CameraSelector.Builder().requireLensFacing(mFacing).build()
// 预览之前先接棒
cameraProvider.unbindAll()
// 将数据绑定到相机的生命周期中
cameraProvider.bindToLifecycle(this@CameraXActivity, cameraSlactor,
preview, imageCapture)
} catch (e: Exception) {
e.printStackTrace()
}
}, ContextCompat.getMainExecutor(this))
}
3、拍摄照片并保存照片图像 使用 imageCapture.takePicture 方法抓取某一帧画面。 由于直接输出的图片方向是摄像头的默认朝向,当拍摄的时候,手机的朝向可以随意,所以要用 fixImageRotation 方法将图片的朝向调整为手机拍照瞬间的方向,再进行照片的保存
fun cameraCapture() {
val dir = File(PATH)
if (!dir.exists()) {
dir.mkdirs()
}
// 创建文件
val photoFile = File(PATH, "testx.jpg")
if (photoFile.exists()) {
photoFile.delete()
}
// 创建包文件的数据
var outputFileOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// 开始拍照
imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor
(this@CameraXActivity), object: OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
runOnUiThread {
val bitmap = fixImageRotation(photoFile)
Toast.makeText(this@CameraXActivity, "保存成功", Toast.LENGTH_LONG).show()
}
}
override fun onError(exception: ImageCaptureException) {
Toast.makeText(this@CameraXActivity, "保存失败", Toast.LENGTH_LONG).show()
}
})
}
fun fixImageRotation(file: File): Bitmap {
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
val exif = ExifInterface(file.absolutePath)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
val matrix = Matrix()
val routationDegress = when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> 90f
ExifInterface.ORIENTATION_ROTATE_90 -> 90f
ExifInterface.ORIENTATION_ROTATE_180 -> 180f
ExifInterface.ORIENTATION_ROTATE_270 -> 270f
else -> 0f
}
matrix.postRotate(routationDegress)
return Bitmap.createBitmap(bitmap, 0, 0 ,bitmap.width, bitmap.height, matrix, true)
}
4、翻转摄像头 使用 CameraSelector 选择需要显示的摄像头类型。前置摄像头,还是后置摄像头
fun switchCamera() {
mFacing = if (mFacing == CameraSelector.LENS_FACING_BACK) CameraSelector
.LENS_FACING_FRONT else CameraSelector.LENS_FACING_BACK
startCamera()
}