学习Android CameraX 顺便翻译一下 官方给的说明
翻译来源CameraX 使用入门 该链接可能需要科学上网
额 前面那几章权限管理 就比较简单了 不翻了 放上布局文件吧 很简单 一个按钮一个 预览的View
<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/camera_capture_button"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
android:elevation="2dp"
android:text="Take Photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
androidx.constraintlayout.widget.ConstraintLayout>
当然AndroidManifest.xml需要注册权限
<!-- 这个貌似是 app需要相机的类型 此处是任何相机 还可以有其他类型 比如需要聚焦 --!>
<uses-feature android:name="android.hardware.Camera.any"/>
<uses-permission android:name="android.permission.CAMERA"/>
以下是翻译正文
在相机应用程序中,取景器用于让用户预览将要拍摄的照片。你可以使用Camerax Preview类来实现一个取景器。
要使用Preview,您首先需要定义一个配置,然后用它来创建用例的一个实例。你需要绑定到CameraX生命周期的结果实例。
1.复制代码到startCamera()函数
下面的分析您刚才复制的代码。
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.Builder().build()
//select back camera
val cameraSelector =
CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
try {
//unbide use cases before rebinding
cameraProvider.unbindAll()
//bind use cases to camera
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
},ContextCompat.getMainExecutor(this))
}
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {},ContextCompat.getMainExecutor(this))
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
preview = Preview.Builder().build()
CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK)
try {
//unbide use cases before rebinding
cameraProvider.unbindAll()
//bind use cases to camera
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
}
catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
2.运行APP,然后你就能看到相机的预览画面
与预览相比,其他用例的工作方式非常类似。首先,您必须定义一个用于实例化实际用例对象的配置对象。要捕获照片,您需要实现takePhoto()方法,该方法在按下捕获按钮时调用
将此代码复制到takePhoto()方法中。
下面的要点将分解您刚才复制的代码。
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 pptions object which constins 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(exception: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
}
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo caputre succeeded: $savedUri"
Toast.makeText(baseContext,msg,Toast.LENGTH_SHORT).show()
Log.d(TAG,msg)
}
}
)
}
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"
)
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback{}
)
override fun onError(exception: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
}
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo caputre succeeded: $savedUri"
Toast.makeText(baseContext,msg,Toast.LENGTH_SHORT).show()
Log.d(TAG,msg)
}
imageCapture=ImageCapture.Builder().build()
这里显示粘贴代码的方法:
private fun startCamera() {
...
preview = Preview.Builder().build()
//Paste image capture code here!
val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
...
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
如果运行Q或更低,那么同时实现预览、图像捕获和图像分析将不适用于Android Studio的设备模拟器。我们建议使用真实的设备来测试这部分代码。
让你的相机应用程序更有趣的一个好方法是使用ImageAnalysis功能。它允许您定义一个实现ImageAnalysis的自定义类。分析器接口,它将被调用与传入的摄像机帧。您将不必担心管理相机会话状态或甚至处理图像;绑定到我们想要的生命周期就足够了,就像其他生命周期感知组件一样。
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) {
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()
}
}
用我们的类实现ImageAnalysis。Analyzer接口,所有我们需要做的是在ImageAnalysis中实例化一个LuminosityAnalyzer的实例,就像所有其他的用例一样,在调用CameraX.bindToLifecycle()之前,再次更新’ startCamera() '函数:
imageAnalyzer = ImageAnalysis.Builder()
.build()
.also {
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
Log.d(TAG, "Average luminosity: $luma")
})
}
这显示了粘贴代码的方法:
private fun startCamera() {
...
imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
// Paste image analyzer code here!
val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
...
}
camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture, imageAnalyzer)
D/CameraXApp: Average luminosity: ...
翻译结束
最后把整个MainActivity.kt放上来
package city.carcate.u.myapplication
import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
/**
*
*/
class MainActivity : AppCompatActivity() {
private var preview: Preview? = null
private var imageCapture: ImageCapture? = null
private var camera: Camera? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PREMISSIONS
)
}
//setup the listener for take photo button
camera_capture_button.setOnClickListener { takePhoto() }
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
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.Builder().build()
imageCapture = ImageCapture.Builder().build()
//select back camera 选择后置摄像头
val cameraSelector =
CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
try {
//unbide use cases before rebinding 先解绑一下
cameraProvider.unbindAll()
//bind use cases to camera
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
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 pptions object which constins 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(exception: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
}
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo caputre succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
}
)
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists()) mediaDir
else filesDir
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_CODE_PREMISSIONS) {
if (allPermissionsGranted()) {
startCamera()
} else {
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_LONG)
.show()
finish()
}
}
}
companion object {
private const val TAG = "CameraXBasic"
private const val FILENAME_FORMAT = "yyyy-MM-mm-ss-SSS" //这个时间怪怪的 正常不是这个吗 yyyy-MM-dd HH:mm:ss
private const val REQUEST_CODE_PREMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
}
还有app配置文件下引入的库
// CameraX core library
def camerax_version = '1.0.0-beta03'
implementation "androidx.camera:camera-core:$camerax_version"
// CameraX Camera2 extensions
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-alpha10'
// CameraX Extensions library
implementation "androidx.camera:camera-extensions:1.0.0-alpha10"