手把手cameraX实现相机预览和拍照

第一步:

在App的build.gradle中的dependencies粘贴一下代码:

// CameraX
//粘贴后记得同步一下
def camerax_version = '1.0.0-alpha06'
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"

第二步:

创建一个activity:

activity的布局代码:




    

    

第三步Activity的代码:

import android.util.Size
import android.graphics.Matrix
import android.util.Rational
import android.view.Surface
import android.view.ViewGroup
import androidx.camera.core.*
import com.luogiant.yuanmao.R
import com.luogiant.yuanmao.basePresenter.BaseActivity
import com.luogiant.yuanmao.constant.Constant
import kotlinx.android.synthetic.main.activity_demo.*
import java.io.File
import java.nio.ByteBuffer
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

/**
 * BaseActivity为自定义activity父类
 * 此处BaseActivity是自己封装的,
 * 本人使用BaseActivity是因为把权限请求封装在里面了
 * 小伙伴可将BaseActivity改为AppCompatActivity
 */
class DemoActivity : BaseActivity() {

    val requestPermissionCode = "com.luogiant.yuanmao.demo.DemoActivity"//申请权限的code
    private val executor = Executors.newSingleThreadExecutor()//单例线程

    override fun setContentView() {
        setContentView(R.layout.activity_demo)

        //此方法存在于BaseActivity中
        //自定义函数检查权限是否拥有
        //在此我不贴出权限请求的代码了
        //因为本文主题是实现cameraX的预览和拍照
        requestMyPermissions(Constant.permissionSType3,requestPermissionCode)    }

    /**
     * 已申请到权限
     */
    override fun hasPermissionS() {
        super.hasPermissionS()
        if (baseRequestPermissionCode.equals(requestPermissionCode))//如果已获取权限,则打开cameraX预览
            startPreView()
    }


    private fun startPreView() {
        viewFinder.post { startCamera() }
        // viewFinder视图改变监听
        viewFinder.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
            updateTransform()
        }
    }

    /**
     * cameraX实现预览
     */
    private fun startCamera() {
        val previewConfig = PreviewConfig.Builder()
//                .setTargetAspectRatioCustom(Rational(9,16))
//                .setTargetAspectRatio(AspectRatio.RATIO_16_9)
                .setTargetResolution(Size(viewFinder.width, viewFinder.height))
                .build()

        val preview = Preview(previewConfig)
        // 取景器更新,重新计算布局
        preview.setOnPreviewOutputUpdateListener {
            // 获取viewFinder父容器
            val parent = viewFinder.parent as ViewGroup
            parent.removeView(viewFinder)//将当前用来预览的TextureView从父容器移除
            parent.addView(viewFinder, 0)//并从新添加用来预览的TextureView

            viewFinder.surfaceTexture = it.surfaceTexture//将预览画面设置为我们的TextureView
            updateTransform()
        }
        //绑定LifeCycle
        CameraX.bindToLifecycle(this, preview,takePhotoListener(),gainAnalyzer())
    }

    /**
     * 根据TextureView重新计算预览尺寸
     */
    private fun updateTransform() {
        val matrix = Matrix()

        val centerX = viewFinder.width / 2f
        val centerY = viewFinder.height / 2f

        // 预览旋转输出
        val rotationDegrees = when(viewFinder.display.rotation) {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> return
        }
        matrix.postRotate(-rotationDegrees.toFloat(), centerX, centerY)

        // 将转换后的画面设置到TextureView
        viewFinder.setTransform(matrix)
    }

    /**
     * 拍照按钮点击监听
     */
    private fun takePhotoListener(): ImageCapture{
        val imageCaptureConfig = ImageCaptureConfig.Builder()
                .setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
                .build()

        val imageCapture = ImageCapture(imageCaptureConfig)

        capture_button.setOnClickListener {
            imageCapture.setTargetAspectRatioCustom(Rational(9,16))//设置拍照保存尺寸
            imageCapture.takePicture(getPathForFile(), executor,
                    object : ImageCapture.OnImageSavedListener {
                        override fun onError(
                                imageCaptureError: ImageCapture.ImageCaptureError,
                                message: String,
                                exc: Throwable?
                        ) {
                            val msg = "拍照失败: $message"
//                            Log.e("CameraXApp", msg, exc)
                            viewFinder.post {
                                toast("拍照失败")
                            }
                        }

                        override fun onImageSaved(file: File) {
//                            val msg = "相片保存在: ${file.absolutePath}"
                            viewFinder.post {
                                toast("拍照成功")
                            }
                        }
                    })
        }
        return imageCapture
    }

    /**
     * 获取拍照储存路径
     */
    private fun getPathForFile(): File {
        val mdks = File(externalCacheDir,"takePhoto")
        if (!mdks.exists())
            mdks.mkdirs()
        val photoFile = File(mdks,"${System.currentTimeMillis()}.jpg")
        if (photoFile.exists())
            photoFile.delete()
        photoFile.createNewFile()
        return photoFile
    }

    private class LuminosityAnalyzer : ImageAnalysis.Analyzer {
        private var lastAnalyzedTimestamp = 0L

        private fun ByteBuffer.toByteArray(): ByteArray {
            rewind()    // 将缓冲区倒回零
            val data = ByteArray(remaining())
            get(data)   // 将缓冲区复制到字节数组中
            return data // 返回字节数组
        }

        override fun analyze(image: ImageProxy, rotationDegrees: Int) {
            val currentTimestamp = System.currentTimeMillis()
            // 计算平均流明的频率不超过每秒一次
            if (currentTimestamp - lastAnalyzedTimestamp >=
                    TimeUnit.SECONDS.toMillis(1)) {
                val buffer = image.planes[0].buffer
                // 从回调对象中提取图像数据
                val data = buffer.toByteArray()
                // 将数据转换为像素值数组
                val pixels = data.map { it.toInt() and 0xFF }
                // 计算图像的平均亮度
                val luma = pixels.average()
                // Log the new luma value
//                Log.d("CameraXApp", "平均亮度: $luma")
                // 更新最后分析帧的时间戳
                lastAnalyzedTimestamp = currentTimestamp
            }
        }
    }

    /**
     * 画面分析器
     * 此分析器可分析出亮度
     * 和解决预览画面扭曲
     */
    private fun gainAnalyzer(): UseCase? {
        val analyzerConfig = ImageAnalysisConfig.Builder().apply {//分析器配置构建
            setImageReaderMode(
                    ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
        }.build()

        //构建图像分析用例并实例化我们的分析器
        val analyzerUseCase = ImageAnalysis(analyzerConfig).apply {
            setAnalyzer(executor, LuminosityAnalyzer())
        }
        return analyzerUseCase//返回分析器
    }
}

你可能感兴趣的:(Android,camera,CameraX)