前语
上一篇简略介绍了自己运用Camera+Surfaceview自界说相机的情况,想了解的可直接前往/post/718533… ,那这一篇持续介绍怎么运用Jetpack的CameraX来自界说相机。
先来说说自己运用下来的相比Camera的一个感触吧:
- 运用简略,无需过多重视相机的装备(这儿就能够解决相机预览变形和旋转的问题)
- 生命周期绑定,这儿能够省去翻开关闭相机和对相机进行生命周期办理的工作
运用CameraX自界说相机也能够简略分为以下几步:
- 权限装备
- 布局装备
- 预览设置
- 摄影设置
第一步 权限装备
运用CameraX所需求的权限和Camera类似,主要是以下几种:
<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" />
android.hardware.camera.any的装备是声明CameraX能够运用设备上的任何一个摄像头,不论是前置仍是后置,和Camera获取一切相机来进行指定类似。因为上述权限涉及到Android6.0及以上的动态权限请求,我个人习惯直接运用Google的easyPermission库进行动态权限请求。获取许到相应的权限才能持续后续的相机定制化运用哟~
第二步 布局装备
既然是自界说相机,那么布局就依据自己的实际情况布局即可。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/view_preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/view_bottom_operate"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#FFF"
android:gravity="center"
android:orientation="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btn_cancle"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:src="@mipmap/icon_cancle"
android:textColor="#000"
android:textSize="18sp" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/btn_take_picture"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:src="@mipmap/icon_take_picture"
android:textColor="#000"
android:textSize="18sp" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text=""
android:textColor="#000"
android:textSize="18sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
第三步 实现预览
实现预览能够分为以下几步:
- 获取相机进程提供者ProcessCameraProvider
- 为相机进程提供者添加监听
- 创立预览窗口
- 指定预览相机
- 绑定相机进程提供者到持有者生命周期
- 添加执行器
具体的每一步对应的代码能够直接参考代码及注释,这儿就不在过多赘述。
// 获取相机进程提供者实例
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
// 为相机进程提供者添加监听
cameraProviderFuture.addListener({
// 获取具体的相机进程提供者
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// 创立相机预览窗口
val preview = Preview.Builder()
.build()
.also {
// 这儿通过咱们布局中的Preview进行预览
it.setSurfaceProvider(binding.viewPreview.surfaceProvider)
}
// 获取用于摄影的实例
imageCapture = ImageCapture.Builder()
.build()
// 指定用于预览的相机,默认为后置相机,假如需求前置相机预览请运用CameraSelector.DEFAULT_FRONT_CAMERA
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 绑定提供者之前先进行解绑,不能重复绑定
cameraProvider.unbindAll()
// 绑定提供者,将相机的生命周期进行绑定,因为camerax具有生命周期感知力,所以消除翻开和关闭相机的任务
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture
)
} catch (e: Exception) {
Log.e(TAG, "相机绑定异常 ${e.message}")
}
}, ContextCompat.getMainExecutor(this))
第四步 摄影
在调用摄影前咱们需求指定一个摄影后的文件存放位置,然后再进行摄影,摄影后咱们能够依据事先指定的路径获取到咱们摄影的文件。因为Android10之后官方逐渐的约束咱们运用File的方法去办理文件,因而这儿咱们运用MediaStore进行。因而摄影相同以下几步:
- 界说相片称号
- 运用MediaStore操作相片
- 装备摄影输出参数
- 开端摄影
1、界说相片称号
这儿为了相片不重复或许不被掩盖,主张运用时刻戳界说文件称号,这样能有效的防止文件被掩盖。
SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA).format(System.currentTimeMillis())
2、运用MediaStore操作相片
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
3、装备摄影输出参数
// 指定输出参数
val outputOptions = ImageCapture.OutputFileOptions
.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
.build()
4、开端摄影
// 开端摄影相片
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
......
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
......
}
}
)
完整摄影代码如下:
/**
* 摄影
*/
private fun takePhoto() {
// 校验是否有可用的相机摄影器
val imageCapture = imageCapture ?: return
// 界说摄影相片称号
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA)
.format(System.currentTimeMillis())
// 运用MediaStore操作相片文件
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
// 指定输出参数
val outputOptions = ImageCapture.OutputFileOptions
.Builder(
contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
.build()
// 开端摄影相片
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
// 摄影失利
val msg = "Photo capture failed: ${exc.message}"
Log.e(TAG, msg, exc)
Toast.makeText(this@CameraActivity, msg, Toast.LENGTH_SHORT).show()
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
// 摄影成功,saveUri便是图片的uri地址
val msg = "Photo capture succeeded: ${output.savedUri}"
val intent = Intent()
val bundle = Bundle()
bundle.putString("picture_uri", output.savedUri.toString())
intent.putExtra("result", bundle)
this@CameraActivity.setResult(Activity.RESULT_OK, intent)
this@CameraActivity.finish()
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
}
)
}
注:通过uri获取到对应相片后,假如要做显现或许上传都相关操作,个人主张进行适当的紧缩处理,毕竟现在的大多数手机摄影出来的相片质量都比较大,操作起来内存毁掉或许网络、时刻耗费都比较大。
最终
demo作用同Camera自界说相机共同,
至此,关于怎么运用Jectpack CameraX自界说相机就介绍完了,关于更多定制化的需求能够依据自己的实际情况进行扩展,如需项目相关Demo能够前往个人码云库房获取: