前几天一个朋友问了我CameraX
怎么手动控制摄像头开关,关闭预览画面。恰好在之前的项目中也有遇到类似的需求。通过本篇文章记录一下CameraX
的简单使用,手动控制摄像头开关以及关闭预览画面。
CameraX简介
CameraX
是一个Jetpck库,提供了一致且易于使用的Api,适用绝大多数Android设备,最低兼容至API 21 即,Android5.0(根据Android Studio的数据,这已经覆盖了市面上98%的设备)。
在项目中添加CameraX
的依赖
在项目app module的build.gradle中的dependencies中添加依赖:
dependencies {
// CameraX core library using the camera2 implementation
def camerax_version = "1.1.0-beta03"
// The following line is optional, as the core library is included indirectly by camera-camera2
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// If you want to additionally use the CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
// If you want to additionally use the CameraX VideoCapture library
implementation "androidx.camera:camera-video:${camerax_version}"
// If you want to additionally use the CameraX View class
implementation "androidx.camera:camera-view:${camerax_version}"
// If you want to additionally use the CameraX Extensions library
implementation "androidx.camera:camera-extensions:${camerax_version}"
}
实现预览、手动控制摄像头开关、关闭预览画面、拍照
CameraX
会按照生命周期自动控制摄像头的开关,可以使用自定义LifecycleOwner
来实现手动控制摄像头开关的功能。
PreviewView
在摄像头关闭后仍会显示最后一帧的画面,我是通过设置PreviewView
的foreground
为全黑来实现关闭预览画面的效果。
实现代码如下:
//自定义控制相机的lifecycle
class CameraLifecycle : LifecycleOwner {
private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
fun cameraOnCreate() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
fun cameraOnStart() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
fun cameraOnResume() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
fun cameraOnPause() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
fun cameraOnStop() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
fun cameraOnDestroyed() {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
//layout中添加PreviewView
<?xml version="1.0" encoding="utf-8"?>
<layout>
<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/pv_camera_preview"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
......
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
//具体实现
class CameraTestActivity : AppCompatActivity() {
private lateinit var binding: LayoutCameraActivityBinding
private lateinit var cameraLifecycle: CameraLifecycle
private lateinit var imageCapture: ImageCapture
private var lastStreamState: PreviewView.StreamState? = null
private val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
bindCamera()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.layout_camera_activity)
cameraLifecycle = CameraLifecycle()
cameraLifecycle.cameraOnCreate()
//申请相机权限
requestPermission.launch(Manifest.permission.CAMERA)
//监听PreviewView的画面输出状态
binding.pvCameraPreview.previewStreamState.observe(this) { streamState ->
Log.i("CameraTest", "previewStreamState :${streamState.name} ,lastStreamState:$lastStreamState")
if (lastStreamState == null) {
lastStreamState = streamState
} else {
when (streamState) {
//输出画面
PreviewView.StreamState.STREAMING -> {
if (lastStreamState != streamState) {
lastStreamState = streamState
//开始输出画面后清空前景
binding.pvCameraPreview.foreground = null
}
}
//停止输出画面
PreviewView.StreamState.IDLE -> {
if (lastStreamState != streamState) {
lastStreamState = streamState
//停止输出画面后仍会停留在最后一帧,设置黑色前景遮挡最后一帧画面
binding.pvCameraPreview.foreground = ContextCompat.getDrawable(this, android.R.color.background_dark)
}
}
else -> {}
}
}
}
binding.btnOpenCamera.setOnClickListener {
//开启摄像头
resumeCamera()
}
binding.btnCloseCamera.setOnClickListener {
//关闭摄像头
pauseCamera()
}
binding.btnTakePhoto.setOnClickListener {
//拍照
val photoFile = getPhotoFile()
photoFile?.let {
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(it).build()
imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Log.i("CameraTest", "take picture succeed savedUri:${outputFileResults.savedUri}")
val savedUri = outputFileResults.savedUri
binding.ivPhoto.setImageURI(savedUri)
}
override fun onError(exception: ImageCaptureException) {
Log.e("CameraTest", "take picture failed error:${exception.message}")
}
})
}
}
}
private fun bindCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
//创建预览
val preview = Preview.Builder()
.build()
//绑定预览View
preview.setSurfaceProvider(binding.pvCameraPreview.surfaceProvider)
//选择前置或者后置摄像头
// CameraSelector.LENS_FACING_FRONT --前置
// CameraSelector.LENS_FACING_BACK --后置
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
//拍照
imageCapture = ImageCapture.Builder()
//设置旋转
.setTargetRotation(binding.pvCameraPreview.display.rotation)
//设置拍照模式
// ImageCaptureCAPTURE_MODE_MINIMIZE_LATENCY --缩短拍摄延迟时间
// ImageCaptureCAPTURE_MODE_MAXIMIZE_QUALITY --优化照片质量
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(cameraLifecycle, cameraSelector, imageCapture, preview)
}, ContextCompat.getMainExecutor(this))
}
private fun getPhotoFile(): File? {
val storageFile: File? =
if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()) {
externalCacheDir
} else {
cacheDir
}
val photoFile = File.createTempFile("tmp_image_file", ".png", storageFile).apply {
createNewFile()
deleteOnExit()
}
return photoFile
}
fun resumeCamera() {
cameraLifecycle.cameraOnStart()
}
fun pauseCamera() {
cameraLifecycle.cameraOnStop()
}
override fun onStart() {
super.onStart()
cameraLifecycle.cameraOnStart()
}
override fun onResume() {
super.onResume()
cameraLifecycle.cameraOnResume()
}
override fun onPause() {
super.onPause()
cameraLifecycle.cameraOnPause()
}
override fun onStop() {
super.onStop()
cameraLifecycle.cameraOnStop()
}
override fun onDestroy() {
super.onDestroy()
cameraLifecycle.cameraOnDestroyed()
}
}
效果如下图: