Kotlin語言+Jetpack的CameraX庫開發(fā)相機功能 第一篇

CameraX的官方介紹
CameraX 是一個 Jetpack 支持庫,旨在幫助您簡化相機應(yīng)用的開發(fā)工作。它提供一致且易用的 API 接口,適用于大多數(shù) Android 設(shè)備,并可向后兼容至 Android 5.0(API 級別 21)。

但是自定義CameraX開發(fā)寫的代碼較多,個人建議使用CameraController——一個Google開發(fā)團隊封裝好的相機管理類。之后的一篇文章是介紹使用CameraControoler和Mlkit庫進行開發(fā)的拍照、錄像和掃描二維碼Demo。

本篇文章只是對CameraX得到拍照功能進行簡單的封裝演示,使用Kotlin+Viewbinding快速開發(fā),歡迎各位指正交流。如果想直接使用CameraController的同學請點擊下一篇文章~~
傳送門:http://www.itdecent.cn/p/e2092b3ab531

想直接下載Demo的同學可以直接點擊:
https://github.com/cgztzero/KotlinCameraXDemo

第一步:引入cameraX官方庫

 // CameraX core library using the camera2 implementation
def camerax_version = "1.1.0-alpha04"
// 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 View class
implementation "androidx.camera:camera-view:1.0.0-alpha24"

第二步:創(chuàng)建布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:id="@+id/buttonLine"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical"
        android:padding="10dp">

        <Button
            android:id="@+id/takePicture"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="拍照" />

        <Button
            android:id="@+id/switchCamera"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:text="切換攝像頭" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/autoFlash"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_weight="1"
                android:text="flash自動" />

            <Button
                android:id="@+id/openFlash"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_weight="1"
                android:text="flash打開" />

            <Button
                android:id="@+id/closeFlash"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_weight="1"
                android:text="flash關(guān)閉" />
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

很簡單的效果


E5ADBD74-90E7-4531-8790-10EF5851D8B6.png

第三步:開啟預(yù)覽+相機設(shè)置
1.需要用到的一些字段

protected var mPreview: Preview? = null //預(yù)覽實例 是一個UseCase
protected lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider> //異步監(jiān)聽對象 監(jiān)聽相機是否準備完成
protected lateinit var cameraExecutor: ExecutorService//相機拍照的線程池
protected lateinit var processCameraProvider: ProcessCameraProvider//當前進程的相機提供者
protected lateinit var cameraSelector: CameraSelector //前置/后置相機選擇對象
protected var imageCapture: ImageCapture? = null//拍照實例 是一個UseCase
protected var outputDirectory: String? = null//相片輸出路徑
protected var imageAnalyzer: ImageAnalysis? = null//相片分析器 可以自定義
protected var lensFacing: Int = -1//記錄是前置or后置攝像頭
protected var mCamera: Camera? = null//打開相機后獲得的相機實例

2.初始化預(yù)覽,預(yù)覽初始化后需要和xml中的預(yù)覽View進行綁定才可以看到畫面

protected fun startPreview() {
 cameraExecutor = Executors.newSingleThreadExecutor()
 cameraProviderFuture = ProcessCameraProvider.getInstance(this)
 cameraProviderFuture.addListener(Runnable {
            //相機準備完畢后進入回調(diào) 初始化一次即可
            processCameraProvider = cameraProviderFuture.get()
            //實例化預(yù)覽對象  注意旋轉(zhuǎn)屏幕需要調(diào)用多次
            bindCameraUseCases()
        }, ContextCompat.getMainExecutor(this))//異步 ContextCompat.getMainExecutor相當于通過handler回調(diào)在主線程
    }

3.bindCameraUseCases()綁定相機實例,這個方法可能被多次調(diào)用(比如在手機切換橫屏和豎屏的時候)

/**
     * 實例化相機
     */
    open fun bindCameraUseCases() {
        //獲得可預(yù)覽對象
        mPreview = Preview.Builder()
            .setTargetAspectRatio(aspectRatio())//比例
            .setTargetRotation(getRotation())//設(shè)置了旋轉(zhuǎn) 就不需要在判斷file里的旋轉(zhuǎn)信息了
            .build()
        //默認選擇后置攝像頭
        if (lensFacing == -1) {
            lensFacing = CameraSelector.LENS_FACING_BACK
        }
        cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
        //保險起見 先解綁所有
        processCameraProvider.unbindAll()
        //初始化拍照實例
        initTakePictureCases()
        // 所有實例綁定到生命周期 就不需要手動釋放相機了
        mCamera = processCameraProvider.bindToLifecycle(
            this,
            cameraSelector,
            mPreview,
            imageCapture,
            imageAnalyzer
        )
        //預(yù)覽加載到view上 子類具體實現(xiàn)
        onPreviewPrepared(mPreview)
    }

4.初始化拍照實例 CamerX庫將拍照,錄像和解析圖形都封裝成了UseCase對象

 /**
     * 初始化拍照實例
     */
    private fun initTakePictureCases() {
        // ImageCapture
        imageCapture = ImageCapture.Builder()
            .setFlashMode(ImageCapture.FLASH_MODE_AUTO)//閃光燈設(shè)置
            .setTargetAspectRatio(aspectRatio())//拍照
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)//低延遲低質(zhì)量 or 高延遲高質(zhì)量
            .setTargetRotation(getRotation())
            .setFlashMode(ImageCapture.FLASH_MODE_AUTO)//閃光燈自動
            .build()

        // ImageAnalysis
        imageAnalyzer = ImageAnalysis.Builder()
            //注意源碼注釋: It is not allowed to set both target aspect ratio and target resolution on the same use case
//            .setTargetResolution(Size(1280, 720))
            .setTargetRotation(aspectRatio())
            .build()
    }

/**
     * 計算比例 文檔里官方寫法
     */
    private fun aspectRatio(): Int {
        val width = getActivityWidth()
        val height = getActivityHeight()
        val previewRatio = max(width, height).toDouble() / min(width, height)
        if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
            return AspectRatio.RATIO_4_3
        }
        return AspectRatio.RATIO_16_9
    }
  
  //根據(jù)view的旋轉(zhuǎn)角度來給相機設(shè)置旋轉(zhuǎn)角度
    private fun getRotation(): Int {
        return window.decorView.display.rotation
    }

5.拍照具體的一些方法調(diào)用

/**
     * 拍照
     */
    private fun takePicture() {
        if (outputDirectory.isNullOrEmpty()) {
            outputDirectory =
                applicationContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath + "/Test/"
            val file = File(outputDirectory)
            if (!file.exists()) {
                file.mkdirs()
            }
        }

        val photoFile = createFile()
        val metadata = ImageCapture.Metadata().apply {
            //水平翻轉(zhuǎn)
            isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
        }

        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
            .setMetadata(metadata)
            .build()

        imageCapture?.takePicture(
            outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    Log.e("ztzt", "Photo capture failed: ${exc.message}", exc)
                }

                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
                    Log.e("ztzt", "Photo capture succeeded: $savedUri")
                }
            })
    }
    
  //預(yù)覽要將xml中的preview和preview實例進行綁定
    override fun onPreviewPrepared(preview: Preview?) {
        super.onPreviewPrepared(preview)
        mPreview?.setSurfaceProvider(binding.previewView.surfaceProvider)
    }

Demo奉上:
1.想要看自定義拍照的的可以看TakePictureActivity
2.想看全功能的可以看CameraControllerActivity
傳送門,
https://github.com/cgztzero/KotlinCameraXDemo
寫的不好,希望大家可以多多交流~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容