CameraX 入门食用方法
CameraX 已经发布了 1.0正式版 对于涉及到使用摄像头的 App , 能否充分利用摄像头有着很大的区别,为此 对 CameraX 进行了解与认知有一定的必要性.
📑即将学会
用 Jetpack 组件支持库 CameraX 创建相机、拍摄、预览
⭕要求
Google官方声明 CameraX 是 Jetpack组件支持库,帮助我们简化相机相关应用的开发工作。并且提供了一致且易用的 API 接口,适用于大多数 Android 设备,并向后兼容至 Android 5.0(API 级别 21)
因此 创建Android应用的时候mininumSDK 需要选择5.0
前置内容
使用 Intent 进行拍照
在应用中拍照的最简便的方法便是使用MediaStore.ACTION_IMAGE_CAPTURE,传入Intent并启动
这会启动系统照相机,并为用户提供一套完整的拍照功能 这时,如果用户授予了用户拍照权限,并对拍照图片满意,我们将在onActivityResult
中获取图片.默认情况下,拍摄的照片会以缩略图的形式返回,使用键data可以获得缩略图
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
而要获取完整图片,需要在启动相机的Intent中,在intent的意图中,添加MediaStore.EXTRA_OUTPUT 参数中设置文件的输出URI,该URI会存储完整的照片
存储后进行完整图片路径后的图片 并进行设置 该存储设置有一定的局限性,请参考官方对拍照的操作 拍照 | Android 开发者 | Android Developers (google.cn)) 这是我们日常中会使用到的一些摄像头获取图片的操作. 而在开发过程中,我们对camera2的API使用时还是会有很多脏代码,而CameraX作为Google推出的jetpack组件.简化了对Camera的开发,让我们看看如何使用CameraX
实战
创建项目
在 App 模块中添加 CameraX 依赖 并同步
使用 PreviewView 实现 CameraX 预览
1.修改布局文件 布局中添加 PreviewView
在布局文件中进行修改
Manifest声明相机权限
<uses-permission android:name="android.permission.CAMERA" />
动态申请相机权限
//覆写onRequestPermissionsResult()方法
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
Toast.makeText(this,
"权限未得到用户授予",
Toast.LENGTH_SHORT).show()
finish()
}
}
2.请求 ProcessCameraProvider
ProcessCameraProvider是一个单例 用于将相机的生命周期绑定到应用程序进程中的任何 生命周期持有者。因为cameraX具有生命周期感应 这可以使我们的应用程序省去打开和关闭相机的任务
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
3.检查 ProcessCameraProvider 可用性
cameraProviderFuture.addListener(Runnable {}, ContextCompat.getMainExecutor(this))
4.ProcessCameraProvider可用后 选择相机并绑定生命周期和用例
- 创建 Preview
- 指定所需的相机
- 将所选相机和任意用例绑定到生命周期
- 将
Preview
连接到PreviewView
cameraProviderFuture.addListener(Runnable {
// 1将camera 绑定到 生命周期持有者
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// 2创建 Preview。
val preview = Preview.Builder()
.build()
.also {
//preview 连接 previewView
it.setSurfaceProvider(findViewById<PreviewView>(R.id.viewFinder).getSurfaceProvider())
}
//3指定所需的相机
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 4绑定前解绑
cameraProvider.unbindAll()
// 5绑定用户用例和相机到生命周期
cameraProvider.bindToLifecycle(
this, cameraSelector, preview)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
效果图
这是camera的预览功能
cameraX相片拍摄
配置应用拍摄图片
- 利用 ImageCapture.Builder 构建 imageCapture
- 绑定用户用例和相机到生命周期 多了一个 imageCapture
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
拍照
- 获取对 ImageCapture 用例的引用
- 创建图片存储容纳文件
- 创建
OutputFileOptions
指定输出方式 输出路径等内容
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(File(...)).build()
- 对
imageCapture
对象调用takePicture()
方法。传入刚才构建的outputOptions
以及保存图片的回调
最终修改代码
cameraProviderFuture.addListener(Runnable {
// 1将camera 绑定到 生命周期持有者
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// 2创建 Preview。
val preview = Preview.Builder()
.build()
.also {
//preview 连接 previewView
it.setSurfaceProvider(findViewById<PreviewView>(R.id.viewFinder).getSurfaceProvider())
}
//todo 新增行
//图像捕获 构建
imageCapture = ImageCapture.Builder()
.build()
//3指定所需的相机
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 4绑定前解绑
cameraProvider.unbindAll()
//todo 新增修改行
// 5绑定用户用例和相机到生命周期
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture)
} catch(exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
private fun takePhoto() {
// 获取图像捕获用例
val imageCapture = imageCapture ?: return
// 创建存储文件对象
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA
).format(System.currentTimeMillis()) + ".jpg")
// 输出条件构建
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// 拍照 传入输出条件 以及拍照回调
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
//异常打印
Log.e(TAG, "拍照失败: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "拍照成功: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
运行展示