<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android 使用 CameraX 實(shí)現(xiàn)拍照和錄制視頻

          共 6711字,需瀏覽 14分鐘

           ·

          2022-05-09 22:22

          原文鏈接: https://juejin.cn/post/6862180757314469901

          AndroidX是Jetpack包下的組件,谷歌幫你考慮好了很多細(xì)節(jié),用就完事了。這些細(xì)節(jié)想自己設(shè)置的話也可以,不設(shè)置使用默認(rèn)值照樣很舒服。

          導(dǎo)包

          implementation?"androidx.camera:camera-camera2:1.0.0-beta07"
          implementation?"androidx.camera:camera-view:1.0.0-alpha14"
          implementation?"androidx.camera:camera-extensions:1.0.0-alpha14"
          implementation?"androidx.camera:camera-lifecycle:1.0.0-beta07"

          請(qǐng)求權(quán)限

          Manifestmanifest 節(jié)點(diǎn)下中加入以下內(nèi)容:


          <uses-permission?android:name="android.permission.CAMERA"?/>

          <uses-feature?android:name="android.hardware.camera.any"?/>

          <uses-permission?android:name="android.permission.WRITE_EXTERNAL_STORAGE"?/>
          <uses-permission?android:name="android.permission.READ_EXTERNAL_STORAGE"?/>

          <uses-permission?android:name="android.permission.RECORD_AUDIO"?/>

          Android Q適配

          manifest 標(biāo)簽里面加入一條屬性:

          ?android:requestLegacyExternalStorage="true"

          畫黃線不理,如果不加這句,在Android Q上會(huì)無法往相冊(cè)存儲(chǔ)文件。

          創(chuàng)建預(yù)覽布局

          這里參考官方Demo的寫法,最底部圖層是一個(gè)PreviewView用來預(yù)覽,上層放兩個(gè)按鈕,一個(gè)用來拍照,一個(gè)用來錄像。


          <androidx.constraintlayout.widget.ConstraintLayout?xmlns:android="http://schemas.android.com/apk/res/android"
          ????xmlns:app="http://schemas.android.com/apk/res-auto"
          ????xmlns:tools="http://schemas.android.com/tools"
          ????android:layout_width="match_parent"
          ????android:layout_height="match_parent"
          ????tools:context=".MainActivity">


          ????<Button
          ????????android:id="@+id/btnStartVideo"
          ????????android:layout_width="100dp"
          ????????android:layout_height="100dp"
          ????????android:layout_marginBottom="50dp"
          ????????android:elevation="2dp"
          ????????android:scaleType="fitCenter"
          ????????android:text="Start?Video"
          ????????app:layout_constraintBottom_toBottomOf="parent"
          ????????app:layout_constraintEnd_toEndOf="parent"
          ????????app:layout_constraintStart_toEndOf="@id/camera_capture_button"?/>


          ????<Button
          ????????android:id="@+id/camera_capture_button"
          ????????android:layout_width="100dp"
          ????????android:layout_height="100dp"
          ????????android:elevation="2dp"
          ????????android:scaleType="fitCenter"
          ????????android:text="Take?Photo"
          ????????app:layout_constraintBottom_toBottomOf="@+id/btnStartVideo"
          ????????app:layout_constraintEnd_toStartOf="@id/btnStartVideo"
          ????????app:layout_constraintStart_toStartOf="parent"?/>


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


          androidx.constraintlayout.widget.ConstraintLayout>

          Activity中申請(qǐng)權(quán)限

          聲明權(quán)限列表

          companion?object?{
          ????private?const?val?REQUEST_CODE_PERMISSIONS?=?10
          ????private?val?REQUIRED_PERMISSIONS?=?arrayOf(Manifest.permission.CAMERA,?Manifest.permission.WRITE_EXTERNAL_STORAGE,
          ????????????????Manifest.permission.READ_EXTERNAL_STORAGE,?Manifest.permission.RECORD_AUDIO)
          }

          判斷當(dāng)前是否已有權(quán)限的方法

          private?fun?allPermissionsGranted()?=?REQUIRED_PERMISSIONS.all?{
          ????ContextCompat.checkSelfPermission(baseContext,?it)?==?PackageManager.PERMISSION_GRANTED
          }

          onCreate 里開始主要邏輯。如果已有權(quán)限,開啟相機(jī)預(yù)覽。

          ?if?(allPermissionsGranted())?{
          ??????startCamera()
          }?else?{
          ??????ActivityCompat.requestPermissions(this,?REQUIRED_PERMISSIONS,?REQUEST_CODE_PERMISSIONS)
          }

          在請(qǐng)求權(quán)限返回的時(shí)候,判斷是否已有權(quán)限,如果有了就可以開啟預(yù)覽了

          override?fun?onRequestPermissionsResult(requestCode:?Int,?permissions:?Array<String>,?grantResults:?IntArray)?{
          ????if?(requestCode?==?REQUEST_CODE_PERMISSIONS)?{
          ????????if?(allPermissionsGranted())?{
          ????????????startCamera()
          ????????}?else?{
          ????????????Toast.makeText(this,?"Permissions?not?granted?by?the?user.",?Toast.LENGTH_SHORT).show()
          ????????????finish()
          ????????}
          ????}
          }

          聲明全局變量

          private?lateinit?var?cameraExecutor:?ExecutorService
          var?cameraProvider:?ProcessCameraProvider??=?null//相機(jī)信息
          var?preview:?Preview??=?null//預(yù)覽對(duì)象
          var?cameraSelector?=?CameraSelector.DEFAULT_BACK_CAMERA//當(dāng)前相機(jī)
          var?camera:?Camera??=?null//相機(jī)對(duì)象
          private?var?imageCapture:?ImageCapture??=?null//拍照用例
          var?videoCapture:?VideoCapture??=?null//錄像用例

          開啟相機(jī)預(yù)覽

          開啟預(yù)覽,把預(yù)覽內(nèi)容放進(jìn) PreviewView 里。

          ????private?fun?startCamera()?{
          ????????cameraExecutor?=?Executors.newSingleThreadExecutor()
          ????????val?cameraProviderFuture?=?ProcessCameraProvider.getInstance(this)
          ????????cameraProviderFuture.addListener(Runnable?{
          ????????????cameraProvider?=?cameraProviderFuture.get()//獲取相機(jī)信息

          ????????????//預(yù)覽配置
          ????????????preview?=?Preview.Builder()
          ????????????????????.build()
          ????????????????????.also?{
          ????????????????????????it.setSurfaceProvider(viewFinder.createSurfaceProvider())
          ????????????????????}

          ????????????imageCapture?=?ImageCapture.Builder().build()//拍照用例配置

          ????????????val?imageAnalyzer?=?ImageAnalysis.Builder()
          ????????????????????.build()
          ????????????????????.also?{
          ????????????????????????it.setAnalyzer(cameraExecutor,?LuminosityAnalyzer?{?luma?->
          ????????????????????????????Log.d(TAG,?"Average?luminosity:?$luma")
          ????????????????????????})
          ????????????????????}

          ????????????cameraSelector?=?CameraSelector.DEFAULT_BACK_CAMERA//使用后置攝像頭
          ????????????videoCapture?=?VideoCapture.Builder()//錄像用例配置
          //????????????????.setTargetAspectRatio(AspectRatio.RATIO_16_9)?//設(shè)置高寬比
          //????????????????.setTargetRotation(viewFinder.display.rotation)//設(shè)置旋轉(zhuǎn)角度
          //????????????????.setAudioRecordSource(AudioSource.MIC)//設(shè)置音頻源麥克風(fēng)
          ????????????????????.build()

          ????????????try?{
          ????????????????cameraProvider?.unbindAll()//先解綁所有用例
          ????????????????camera?=?cameraProvider?.bindToLifecycle(this,?cameraSelector,?preview,?imageCapture,?videoCapture)//綁定用例
          ????????????}?catch?(exc:?Exception)?{
          ????????????????Log.e(TAG,?"Use?case?binding?failed",?exc)
          ????????????}

          ????????},?ContextCompat.getMainExecutor(this))
          ????}

          監(jiān)聽,寫在Activity外面

          typealias?LumaListener?=?(luma:?Double)?->?Unit

          再在Activity里面寫一個(gè)內(nèi)部類

          private?class?LuminosityAnalyzer(private?val?listener:?LumaListener)?:?ImageAnalysis.Analyzer?{

          ????????private?fun?ByteBuffer.toByteArray():?ByteArray?{
          ????????????rewind()
          ????????????val?data?=?ByteArray(remaining())
          ????????????get(data)
          ????????????return?data
          ????????}

          ????????override?fun?analyze(image:?ImageProxy)?{

          ????????????val?buffer?=?image.planes[0].buffer
          ????????????val?data?=?buffer.toByteArray()
          ????????????val?pixels?=?data.map?{?it.toInt()?and?0xFF?}
          ????????????val?luma?=?pixels.average()

          ????????????listener(luma)

          ????????????image.close()
          ????????}
          ????}

          界面銷毀時(shí)關(guān)閉線程

          override?fun?onDestroy()?{
          ????super.onDestroy()
          ????cameraExecutor.shutdown()
          }

          拍照方法

          private?fun?takePhoto()?{
          ????val?imageCapture?=?imageCapture??:?return
          ????val?file?=?File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path?+
          ????????????"/CameraX"?+?SimpleDateFormat(FILENAME_FORMAT,?Locale.CHINA).format(System.currentTimeMillis())?+?".jpg")
          ????val?outputOptions?=?ImageCapture.OutputFileOptions.Builder(file).build()
          ????imageCapture.takePicture(outputOptions,?ContextCompat.getMainExecutor(this),
          ????????????object?:?ImageCapture.OnImageSavedCallback?{
          ????????????????override?fun?onError(exc:?ImageCaptureException)?{
          ????????????????????Log.e(TAG,?"Photo?capture?failed:?${exc.message}",?exc)
          ????????????????}
          ????????????????override?fun?onImageSaved(output:?ImageCapture.OutputFileResults)?{
          ????????????????????val?savedUri?=?Uri.fromFile(file)
          ????????????????????val?msg?=?"Photo?capture?succeeded:?$savedUri"
          ????????????????????Toast.makeText(baseContext,?msg,?Toast.LENGTH_SHORT).show()
          ????????????????????Log.d(TAG,?msg)
          ????????????????}
          ????????????})
          }

          錄像方法

          private?fun?takeVideo()?{
          ????//視頻保存路徑
          ????val?file?=?File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path?+?"/CameraX"?+?SimpleDateFormat(
          ????????????FILENAME_FORMAT,?Locale.CHINA
          ????).format(System.currentTimeMillis())?+?".mp4")
          ????//開始錄像
          ????videoCapture?.startRecording(file,?Executors.newSingleThreadExecutor(),?object?:?OnVideoSavedCallback?{
          ????????override?fun?onVideoSaved(@NonNull?file:?File)?{
          ????????????//保存視頻成功回調(diào),會(huì)在停止錄制時(shí)被調(diào)用
          ????????????Toast.makeText(this@MainActivity,?file.absolutePath,?Toast.LENGTH_SHORT).show()
          ????????}
          ????????override?fun?onError(videoCaptureError:?Int,?message:?String,?cause:?Throwable?)?{
          ????????????//保存失敗的回調(diào),可能在開始或結(jié)束錄制時(shí)被調(diào)用
          ????????????Log.e("",?"onError:?$message")
          ????????}
          ????})


          ????btnStartVideo.setOnClickListener?{
          ????????//結(jié)束錄像
          ????????videoCapture?.stopRecording()//停止錄制
          ????????preview?.clear()//清除預(yù)覽
          ????????btnStartVideo.text?=?"Start?Video"
          ????????btnStartVideo.setOnClickListener?{
          ????????????btnStartVideo.text?=?"Stop?Video"
          ????????????takeVideo()
          ????????}
          ????????Toast.makeText(this,?file.path,?Toast.LENGTH_SHORT).show()
          ????????Log.d("path",?file.path)
          ????}
          }

          文中代碼完整Demo

          Github CameraX-Demo?https://github.com/DubheBroken/CameraX-Demo

          參考文章:

          Google CameraX 開發(fā)文檔 ?
          掘金-JetPack之使用CameraX完成拍照和拍視頻 ?
          Stackoverflow-Exception 'open failed: EACCES (Permission denied)' on Android


          瀏覽 178
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品人妻一区二区三区日产乱码 | 91.xxxxx | 日韩永久免费A片 | 亚洲第一黄色电影 | 国产成人三级在线观看视频 |