Android CameraX —手动控制摄像头开关

1,842 阅读2分钟

前几天一个朋友问了我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在摄像头关闭后仍会显示最后一帧的画面,我是通过设置PreviewViewforeground为全黑来实现关闭预览画面的效果。

实现代码如下:

//自定义控制相机的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()
    }
}

效果如下图:

d1785b55f2607372f87271547270ed11_.gif