CameraX 是一個(gè) Jetpack 支持庫,旨在幫助您簡化相機(jī)應(yīng)用的開發(fā)工作。它提供一致且易于使用的 API 界面,適用于大多數(shù) Android 設(shè)備麦轰,并可向后兼容至 Android 5.0(API 級別 21)。
雖然它利用的是 camera2 的功能砖织,但使用的是更為簡單且基于用例的方法款侵,該方法具有生命周期感知能力。它還解決了設(shè)備兼容性問題镶苞,因此您無需在代碼庫中包含設(shè)備專屬代碼喳坠。這些功能減少了將相機(jī)功能添加到應(yīng)用時(shí)需要編寫的代碼量。
目前處于 Alpha 版測試階段茂蚓,因?yàn)槠?API 界面尚未最終確定。我們不建議在生產(chǎn)環(huán)境中使用 Alpha 庫剃幌。CameraX 庫應(yīng)在生產(chǎn)環(huán)境中嚴(yán)格避免依賴 Alpha 庫聋涨,因?yàn)槠?API 界面可能會以與源代碼和二進(jìn)制文件不兼容的方式發(fā)生變化。
相比較于使用Camera2預(yù)覽负乡、拍照時(shí)大量的接口牍白、回調(diào),使用CameraX基本可以使用不超過100行代碼實(shí)現(xiàn)相同功能抖棘。雖然目前仍是測試版本茂腥,但個(gè)人強(qiáng)烈建議先學(xué)習(xí)下,CameraX 真的超簡單切省,超好用W罡凇!朝捆!后面正式版發(fā)布后就可以隨時(shí)使用般渡。
CameraX使用
CameraX 結(jié)構(gòu)
開發(fā)者使用 CameraX,借助名為“用例”的抽象概念與設(shè)備的相機(jī)進(jìn)行交互。目前提供的用例如下:
- 預(yù)覽:準(zhǔn)備一個(gè)預(yù)覽 SurfaceTexture
- 圖片拍攝:拍攝并保存照片
- 圖片分析:提供 CPU 可訪問的緩沖區(qū)以進(jìn)行分析(例如進(jìn)行機(jī)器學(xué)習(xí))
不同用例可以相互組合使用驯用,也可以同時(shí)處于活動狀態(tài)脸秽。例如,用戶可以在應(yīng)用中使用預(yù)覽用例查看進(jìn)入相機(jī)視野的畫面蝴乔、加入圖片分析用例來確定照片里的人物是否在微笑记餐,以及包含一個(gè)圖片拍攝用例以便在人物微笑時(shí)拍攝照片。
添加依賴
def camerax_version = "1.0.0-beta04"
// CameraX core library using camera2 implementation
implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX Lifecycle Library
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class
implementation "androidx.camera:camera-view:1.0.0-alpha11"
布局
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
使用androidx.camera.view.PreviewView類薇正。它是CameraX中顯示預(yù)覽用例的自定義視圖片酝。該類管理Surface生命周期,以及預(yù)覽縱橫比和方向铝穷。在它內(nèi)部使用TextureView或SurfaceView來顯示钠怯。
實(shí)現(xiàn)預(yù)覽
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview
preview = Preview.Builder()
.build()
// Select back camera
val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview)
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider())
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
就這樣就可以實(shí)現(xiàn)Camera的預(yù)覽功能,是不是很簡單曙聂,想起之前寫Camera2的痛苦晦炊,眼淚都快流下來了。
拍照
//在startCamera中增加
// ImageCapture
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
private fun takePhoto() {
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create timestamped output file to hold the image
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA
).format(System.currentTimeMillis()) + ".jpg")
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Setup image capture listener which is triggered after photo has
// been taken
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(photoFile)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
//最后綁定到Camera上
// Bind use cases to camera
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview,imageCapture)
完成了宁脊,這代碼簡潔程度簡直愛了断国!
圖片分析
寫一個(gè)內(nèi)部類,繼承ImageAnalysis.Analyzer
private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {
private fun ByteBuffer.toByteArray(): ByteArray {
rewind() // Rewind the buffer to zero
val data = ByteArray(remaining())
get(data) // Copy the buffer into a byte array
return data // Return the byte array
}
override fun analyze(image: ImageProxy) {
//處理圖片數(shù)據(jù)
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()
}
}
imageAnalyzer = ImageAnalysis.Builder()
.build()
.also {
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
Log.d(TAG, "Average luminosity: $luma")
})
}
綁定設(shè)備
// Bind use cases to camera
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, imageAnalyzer)
仍然是這么簡單榆苞!等CameraX正式版本發(fā)布稳衬,Camera2就扔到垃圾桶去吧。
記得申請權(quán)限白薄疚!Manifest.permission.CAMERA
可以翻墻的請看原文:Getting Started with CameraX