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>
很簡單的效果

第三步:開啟預(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
寫的不好,希望大家可以多多交流~