东智哥 發表於 2021-8-12 15:24:00

Android开发 CameraX开发

<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">  google推出Camera后,发现Camera功能简单,难以满足需求调用Camera各种效果,所以又推出了Camera2. Camera2功能强大但是使用十分麻烦,回调与冗余代码太多,而且特别容易在释放Camera上犯错导致activty的内存泄露. 所以google推出了更简单易用,但是功能也强大的CameraX.</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">  因为CameraX的简单可以帮助我们高效率开发,所以也是有学习的必要性.(Camera2了解就行,没必要死磕浪费太多时间),CameraX有以下优势:</span></p>
<ol>
<li><span style="color: rgba(0, 0, 0, 1)">CameraX与Liftcycle结合,与Activity或者Fragment的生命周期捆绑,不要考虑摄像头的释放问题,减少了代码的复杂度.</span></li>
<li><span style="color: rgba(0, 0, 0, 1)">CameraX兼容至&nbsp;Android L (API 21)</span></li>
<li><span style="color: rgba(0, 0, 0, 1)">依然支持Camera2的丰富摄像头功能</span></li>
</ol>
<h1><span style="color: rgba(0, 128, 128, 1)">添加依赖</span></h1>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> CameraX 核心库使用 camera2 实现</span>
    implementation "androidx.camera:camera-camera2:1.0.0-beta03"
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以使用CameraView</span>
    implementation "androidx.camera:camera-view:1.0.0-alpha10"
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以使用供应商扩展</span>
    implementation "androidx.camera:camera-extensions:1.0.0-alpha10"
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">camerax的生命周期库</span>
    implementation "androidx.camera:camera-lifecycle:1.0.0-beta03"</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">获取权限</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">跟以前一样,需要动态授权一些必要权限</span></p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 相机相关 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.WRITE_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.CAMERA"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.WRITE_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.READ_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">预览摄像头画面</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">从最简单的预览摄像头图像开始,我们逐步了解使用方式,代码如下:</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">布局要求使用</span>PreviewView,作为SurfaceProvider</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">androidx.camera.view.PreviewView
      </span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/previewView"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span></pre>
</div>
<p><span style="color: rgba(0, 0, 0, 1)">代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CameraXActivity : AppCompatActivity() {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val TAG = CameraXActivity::<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.java.simpleName
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mPreviewView: PreviewView<br>
    override fun onCreate(savedInstanceState: Bundle</span>?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
      setContentView(R.layout.activity_camera_x2)
      mPreviewView </span>=<span style="color: rgba(0, 0, 0, 1)"> findViewById(R.id.previewView)
      startCameraPreview()
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * 开始相机预览
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun startCameraPreview() {
      val cameraProviderFuture </span>= ProcessCameraProvider.getInstance(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
      cameraProviderFuture.addListener(Runnable {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用于将相机的生命周期绑定到生命周期所有者</span>
            val cameraProvider: ProcessCameraProvider =<span style="color: rgba(0, 0, 0, 1)"> cameraProviderFuture.get()
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建预览</span>
            val preview =<span style="color: rgba(0, 0, 0, 1)"> Preview.Builder().build()
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">选择后置摄像头</span>
            val cameraSelector =<span style="color: rgba(0, 0, 0, 1)"> CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在重新绑定之前取消绑定</span>
<span style="color: rgba(0, 0, 0, 1)">                cameraProvider.unbindAll()
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将生命周期,选择摄像头,预览,绑定到相机</span>
                val camera = cameraProvider.bindToLifecycle(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, cameraSelector, preview)
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置预览的View</span>
<span style="color: rgba(0, 0, 0, 1)">                preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera.cameraInfo))
            } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (exc: Exception) {
                Log.e(TAG, </span>"Use case binding failed"<span style="color: rgba(0, 0, 0, 1)">, exc)
            }
      }, ContextCompat.getMainExecutor(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">))
    }
}</span></pre>
</div>
<p>特别简单就完成了,而且无需考虑摄像头的释放</p>
<h1><span style="color: rgba(0, 128, 128, 1)">实现拍照</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CameraXActivity : AppCompatActivity() {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val TAG = CameraXActivity::<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.java.simpleName
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mImageCapture: ImageCapture
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mImageAnalysis: ImageAnalysis
    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mPreviewView: PreviewView
    override fun onCreate(savedInstanceState: Bundle</span>?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
      setContentView(R.layout.activity_camera_x2)
      mPreviewView </span>=<span style="color: rgba(0, 0, 0, 1)"> findViewById(R.id.previewView)
      startCameraPreview()
      takePhoto.setOnClickListener {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">点击后拍照</span>
<span style="color: rgba(0, 0, 0, 1)">            takePhoto()
      }
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * 开始相机预览
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun startCameraPreview() {
      val cameraProviderFuture </span>= ProcessCameraProvider.getInstance(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
      cameraProviderFuture.addListener(Runnable {
            val cameraProvider: ProcessCameraProvider </span>=<span style="color: rgba(0, 0, 0, 1)"> cameraProviderFuture.get()
            val preview </span>=<span style="color: rgba(0, 0, 0, 1)"> Preview.Builder().build()
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
            mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder().build()
            val cameraSelector </span>=<span style="color: rgba(0, 0, 0, 1)"> CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
            </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
                cameraProvider.unbindAll()
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">请注意,这里新增了一个ImageCapture</span>
                val camera = cameraProvider.bindToLifecycle(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, cameraSelector, preview, mImageCapture)
                preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera.cameraInfo))
            } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (exc: Exception) {
                Log.e(TAG, </span>"Use case binding failed"<span style="color: rgba(0, 0, 0, 1)">, exc)
            }
      }, ContextCompat.getMainExecutor(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">))
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * 拍照
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun takePhoto() {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">图像的保存路径与名称</span>
      val photoFile = File(applicationContext.externalCacheDir?<span style="color: rgba(0, 0, 0, 1)">.path
                , SimpleDateFormat(</span>"yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(System.currentTimeMillis()) + ".jpg"<span style="color: rgba(0, 0, 0, 1)">)

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建图像文件输出选项</span>
      val outputOptions =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.OutputFileOptions.Builder(photoFile).build()

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">拍照,并且注册拍照后的结果监听</span>
      mImageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">), object : ImageCapture.OnImageSavedCallback {
            override fun onError(exc: ImageCaptureException) {
                Log.e(TAG, </span>"Photo capture failed: ${exc.message}"<span style="color: rgba(0, 0, 0, 1)">, exc)
            }

            override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                val savedUri </span>=<span style="color: rgba(0, 0, 0, 1)"> Uri.fromFile(photoFile)
                val msg </span>= "Photo capture succeeded: $savedUri"<span style="color: rgba(0, 0, 0, 1)">
                Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                Log.d(TAG, msg)
            }
      })
    }
}</span></pre>
</div>
<h2>拍照图像旋转</h2>
<p>代码的其他部分与上面的示例代码一致, 我们只需要关注ImageCapture的创建与添加setTargetRotation</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
      mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
                .setTargetRotation(Surface.ROTATION_90)</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置旋转角度,并且只能有4个旋转方向属性ROTATION_0/ROTATION_90/ROTATION_180/ROTATION_270</span>
<span style="color: rgba(0, 0, 0, 1)">                .build()
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
    }</span></pre>
</div>
<h2>设置执行IO线程</h2>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
      mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
                .setIoExecutor(Executors.newSingleThreadExecutor())</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置执行IO线程</span>
<span style="color: rgba(0, 0, 0, 1)">                .build()
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
    }</span></pre>
</div>
<h2>设置捕捉模式</h2>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
      mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">CAPTURE_MODE_MAXIMIZE_QUALITY 高画质
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">CAPTURE_MODE_MINIMIZE_LATENCY 低延迟</span>
<span style="color: rgba(0, 0, 0, 1)">                .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
                .build()
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
    }</span></pre>
</div>
<h2>设置闪光灯</h2>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
      mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FLASH_MODE_ON 闪光灯开启
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FLASH_MODE_OFF 闪光灯关闭
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FLASH_MODE_AUTO 闪光灯自动</span>
<span style="color: rgba(0, 0, 0, 1)">                .setFlashMode(ImageCapture.FLASH_MODE_ON)
                .build()
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
    }</span></pre>
</div>
<h2>设置宽高比</h2>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
      mImageCapture </span>=<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">RATIO_4_3   4比3
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">RATIO_16_916比9</span>
<span style="color: rgba(0, 0, 0, 1)">                .setTargetAspectRatio(AspectRatio.RATIO_16_9)
                .build()
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
    }</span></pre>
</div>
<h2>设置分辨率</h2>
<p>下面还帖了一些注释,这注释的意思是,你可以随便设置分辨率大小,但是真正的分辨率并不一定是你设置的数值,而是在摄像头里获取的分辨率列表中去取最近似值.</p>
<p>为什么会有这种说明? 我这里给没有接触过摄像头开发的朋友说明一下:</p>
<p>手机的摄像头的分辨率并不是可以随便设置的,这需要取决于你开发的设备的摄像头驱动的分辨率列表. 在以往开发Camera1和Camera2的时候我们需要自己获取这份列表,从中选择我们需要的分辨率. 在使用CameraX的时候他们帮我们简化了这个筛选过程,你只需要设置目标分辨率,代码会自动选择近似分辨率</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture(): ImageCapture {
      mImageCapture </span>=<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
                </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
                目标分辨率尝试建立图像分辨率的最小界限。实际图像分辨率将是尺寸上最接近的可用分辨率,该分辨率不小于由相机实现确定的目标分辨率。
                但是,如果不存在等于或大于目标分辨率的分辨率,则将选择最接近的小于目标分辨率的可用分辨率。
                与提供的 {@link Size} 具有相同纵横比的分辨率将在不同纵横比的分辨率之前优先考虑。
               </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                .setTargetResolution(Size(</span>1280, 720<span style="color: rgba(0, 0, 0, 1)">))
                .build()
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">控制对焦</span></h1>
<div class="cnblogs_code">
<pre>val factory =<span style="color: rgba(0, 0, 0, 1)"> SurfaceOrientedMeteringPointFactory(width, height)
val point </span>=<span style="color: rgba(0, 0, 0, 1)"> factory.createPoint(x, y)
val action </span>=<span style="color: rgba(0, 0, 0, 1)"> FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
    .addPoint(point2, FocusMeteringAction.FLAG_AE) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> could have many
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> auto calling cancelFocusAndMetering in 5 seconds</span>
    .setAutoCancelDuration(5<span style="color: rgba(0, 0, 0, 1)">, TimeUnit.SECONDS)
    .build()

val future </span>=<span style="color: rgba(0, 0, 0, 1)"> cameraControl.startFocusAndMetering(action)
future.addListener( Runnable {
    val result </span>=<span style="color: rgba(0, 0, 0, 1)"> future.get()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> process the result</span>
} , executor)</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">实现录像</span></h1>
<div class="cnblogs_code">
<pre>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> takeVideo() {
      VideoCapture videoCapture </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> VideoCaptureConfig.Builder()
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置宽高</span>
<span style="color: rgba(0, 0, 0, 1)">                .setTargetAspectRatio(aspectRatio(width, height))
                </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置旋转角度</span>
<span style="color: rgba(0, 0, 0, 1)">                .setTargetRotation(previewView.getDisplay().getRotation())
                .build();
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">录像前必须解绑</span>
<span style="color: rgba(0, 0, 0, 1)">      cameraProvider.unbindAll();
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">开启相机预览</span>
<span style="color: rgba(0, 0, 0, 1)">      preview.setSurfaceProvider(previewView.createSurfaceProvider());
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">绑定生命周期,这里如果没有参数preview,则只录像,不显示画面</span>
      cameraProvider.bindToLifecycle(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, cameraSelector,preview, videoCapture);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">视频路径</span>
      File file = getFile(".mp4"<span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">开始录像</span>
      videoCapture.startRecording(file, ContextCompat.getMainExecutor(MainActivity.<span style="color: rgba(0, 0, 255, 1)">this</span>), <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> VideoCapture.OnVideoSavedCallback() {
            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onVideoSaved(@NonNull File file) {
                Toast.makeText(MainActivity.</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
            }

            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onError(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
                Log.d(TAG, </span>"onError: " +<span style="color: rgba(0, 0, 0, 1)"> message);
            }
      });
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">停止录像,并且回调OnVideoSavedCallback</span>
      btn4.setOnClickListener(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> View.OnClickListener() {
            @Override
            </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onClick(View v) {
                videoCapture.stopRecording();
                preview.clear();
            }
      });
    </span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">分析图像</span></h1>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre>val imageAnalysis =<span style="color: rgba(0, 0, 0, 1)"> ImageAnalysis.Builder()
    .setTargetResolution(Size(</span>1280, 720<span style="color: rgba(0, 0, 0, 1)">))
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
    .build()

imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), ImageAnalysis.Analyzer { image </span>-&gt;<span style="color: rgba(0, 0, 0, 1)">
    val rotationDegrees </span>= image.imageInfo.rotationDegrees <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">旋转角度</span>
    val format = image.format <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">格式</span>
    val width = image.width <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">宽</span>
    val height = image.height <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">高</span>
    val plane = image.planes <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PlaneProxy数据</span>
    val buffer =<span style="color: rgba(0, 0, 0, 1)"> plane.buffer
    Log.e(</span>"ytzn", "rotationDegrees = $rotationDegrees"<span style="color: rgba(0, 0, 0, 1)">)
    Log.e(</span>"ytzn", "format = $format"<span style="color: rgba(0, 0, 0, 1)">)
    Log.e(</span>"ytzn", "width = $width"<span style="color: rgba(0, 0, 0, 1)">)
    Log.e(</span>"ytzn", "height = $height"<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> insert your code here.</span>
<span style="color: rgba(0, 0, 0, 1)">})
cameraProvider.bindToLifecycle(</span><span style="color: rgba(0, 0, 255, 1)">this</span> as LifecycleOwner, cameraSelector, imageAnalysis, preview)</pre>
</div>
<p>CameraX 会生成&nbsp;<code dir="ltr">YUV_420_888</code>&nbsp;格式的图片 在ImageFormat类里可以看到此格式</p>
<h1><span style="color: rgba(0, 128, 128, 1)">拓展功能</span></h1>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun setPreviewExtender(builder: Preview.Builder, cameraSelector: CameraSelector) {
      val extender </span>=<span style="color: rgba(0, 0, 0, 1)"> AutoPreviewExtender.create(builder)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">自动模式</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (extender.isExtensionAvailable(cameraSelector)) {
            extender.enableExtension(cameraSelector)
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">散景扩展</span>
      val bokehPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> BokehPreviewExtender.create(builder)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (bokehPreviewExtender.isExtensionAvailable(cameraSelector)) {
            bokehPreviewExtender.enableExtension(cameraSelector)
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">hdr扩展</span>
      val hdrPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> HdrPreviewExtender.create(builder)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hdrPreviewExtender.isExtensionAvailable(cameraSelector)) {
            hdrPreviewExtender.enableExtension(cameraSelector)
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">美颜模式</span>
      val beautyPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> BeautyPreviewExtender.create(builder)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (beautyPreviewExtender.isExtensionAvailable(cameraSelector)) {
            beautyPreviewExtender.enableExtension(cameraSelector)
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">夜晚模式</span>
      val nightPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> NightPreviewExtender.create(builder)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (nightPreviewExtender.isExtensionAvailable(cameraSelector)) {
            nightPreviewExtender.enableExtension(cameraSelector)
      }

    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">CameraX的一些问题</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">个人在开发过程中发现了一些问题,如下:</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">1.CameraX不支持外置摄像头</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">2.一直没找到跟Camera2一样配置摄像头拍照参数的方式</span></p>
<p>&nbsp;</p>
<p><span style="color: rgba(0, 128, 128, 1)">End</span></p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/15132909.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/15132909.html
頁: [1]
查看完整版本: Android开发 CameraX开发