美文网首页
CameraX实现预览和拍照

CameraX实现预览和拍照

作者: __xlg | 来源:发表于2020-01-10 17:53 被阅读0次

    第一步build.gradle:

        // CameraX
        def camerax_version = '1.0.0-alpha06'
        implementation "androidx.camera:camera-core:${camerax_version}"
        implementation "androidx.camera:camera-camera2:${camerax_version}"
    

    第二步布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        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">
    
        <TextureView
            android:id="@+id/viewFinder"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <Button
            android:id="@+id/capture_button"
            android:text="拍照"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="24dp"
            android:layout_centerInParent="true"
            android:layout_alignParentBottom="true" />
    
    </RelativeLayout>
    

    第三步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中
            //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//返回分析器
        }
    }
    
    

    相关文章

      网友评论

          本文标题:CameraX实现预览和拍照

          本文链接:https://www.haomeiwen.com/subject/jgrmactx.html