Google 原来提供了Camera,后面又推出了Camera2,然后几年前在Jetpack里面推出了CameraX
CameraX其实内部是封装Camera2的。但是它比Camera2使用起来更加简便,性能也比Camera2提高了。
废话少说,直接上代码:
要用CameraX,第一是要引入其 dependencies
// CameraX core library
def camerax_version = '1.1.0-beta01'
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:$camerax_version"
进而上个布局;
<?xml version="1.0" encoding="utf-8"?>
<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=".CameraActivity">
<!--预览的控件-->
<androidx.camera.view.PreviewView
android:id="@+id/preview_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:scaleType="fillCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/switchCamera"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:text="切换相机"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.appcompat.widget.AppCompatButton>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvFps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/switchCamera"
app:layout_constraintLeft_toLeftOf="parent"
android:textSize="20sp"
android:gravity="center"
android:padding="5dp"
android:text="12456"
android:textColor="@color/white">
</androidx.appcompat.widget.AppCompatTextView>
</androidx.constraintlayout.widget.ConstraintLayout>
然后上个Activity代码:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import android.view.View
import androidx.appcompat.widget.AppCompatTextView
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.content.ContextCompat
class CameraActivity : AppCompatActivity() {
companion object{
private const val TAG = "CameraActivity"
}
private var cameraProvider: ProcessCameraProvider? = null
private var previewUseCase: Preview? = null
private lateinit var previewView: PreviewView
private lateinit var tvFps: AppCompatTextView
private var cameraSelector: CameraSelector? = null
private var lensFacing = CameraSelector.LENS_FACING_FRONT
private var camera: Camera? = null
private var analysisUseCase: ImageAnalysis? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
previewView = findViewById(R.id.preview_view)
tvFps = findViewById(R.id.tvFps)
findViewById<View>(R.id.switchCamera).setOnClickListener {
switchCamera()
}
cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
if (PermissionsUtil.allPermissionsGranted(this)){
start()
}else{
PermissionsUtil.requestPermission(this)
}
}
private fun start() {
/*
通过 ProcessCameraProvider 获取 cameraProvider
cameraProvider 就是我们持有的相机实例
*/
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
cameraProvider = cameraProviderFuture.get()
startCamera()
}, ContextCompat.getMainExecutor(this))
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (PermissionsUtil.allPermissionsGranted(this)){
start()
}
}
/**
* 启动相机
* */
private fun startCamera() {
if (cameraProvider != null) {
cameraProvider!!.unbindAll()
/*
这一步是绑定预览界面,如果不需要预览界面,这一步克注释掉
CameraX优势体验之一:预览界面可以根据开发者需求去取舍,而Camera1和Camera2则必须要预览界面
*/
bindPreviewUseCase()
// 这一步是绑定相机预览数据,可以获得相机每一帧的数据
bindAnalysisUseCase()
}
}
/**
* 绑定预览界面。不需要预览界面可以不调用
* */
private fun bindPreviewUseCase() {
if (previewUseCase != null) {
cameraProvider!!.unbind(previewUseCase)
}
val builder = Preview.Builder()
builder.setTargetAspectRatio(AspectRatio.RATIO_16_9).build()
previewUseCase = builder.build()
previewUseCase!!.setSurfaceProvider(previewView.surfaceProvider)
cameraProvider!!.bindToLifecycle(
this,
cameraSelector!!,
previewUseCase
)
}
private var lastTime = 0L
private var lastShowTime = 0L
private var count = 0
private var maxFrameMs = 0L
private var minFrameMs = Long.MAX_VALUE
private fun bindAnalysisUseCase() {
if (analysisUseCase != null) {
cameraProvider!!.unbind(analysisUseCase)
}
val builder = ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9)
analysisUseCase = builder.build()
analysisUseCase?.setAnalyzer(
ContextCompat.getMainExecutor(this)
) { imageProxy: ImageProxy ->
count += 1
val currentTime = SystemClock.elapsedRealtime()
val d = currentTime - lastShowTime
maxFrameMs = maxFrameMs.coerceAtLeast(d)
minFrameMs = minFrameMs.coerceAtMost(d)
if ((currentTime - lastTime) >= 1000){
tvFps.text = "fps: ${count}, 两帧最大:${maxFrameMs}, 最小:${minFrameMs}"
lastTime = currentTime
count = 0
maxFrameMs = 0
minFrameMs = Long.MAX_VALUE
}
lastShowTime = currentTime
Log.d(TAG,"ImageProxy: 宽高: ${imageProxy.width} * ${imageProxy.height}")
//必须close,相机才会下发下一帧数据,否则会一直阻塞相机下发数据
imageProxy.close()
}
camera = cameraProvider!!.bindToLifecycle(
this,
cameraSelector!!,
analysisUseCase
)
}
private fun switchCamera() {
if (cameraProvider == null) {
return
}
val newLensFacing =
if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
CameraSelector.LENS_FACING_BACK
} else {
CameraSelector.LENS_FACING_FRONT
}
val newCameraSelector = CameraSelector.Builder().requireLensFacing(newLensFacing).build()
try {
if (cameraProvider!!.hasCamera(newCameraSelector)) {
lensFacing = newLensFacing
cameraSelector = newCameraSelector
startCamera()
return
}
} catch (e: CameraInfoUnavailableException) {
// Falls through
}
}
}
添加权限工具类代码
object PermissionsUtil {
private const val TAG = "PermissionsUtil"
private const val PERMISSION_REQUESTS = 1
fun requestPermission(context: Context) {
if (!allPermissionsGranted(context)) {
runtimePermissions(context)
}
}
private fun isPermissionGranted(context: Context, permission: String?): Boolean {
if (ContextCompat.checkSelfPermission(context, permission!!) ==
PackageManager.PERMISSION_GRANTED
) {
return true
}
return false
}
fun allPermissionsGranted(context: Context): Boolean {
for (permission in requiredPermissions(context)) {
if (!isPermissionGranted(context, permission)) {
return false
}
}
return true
}
private fun requiredPermissions(context: Context): Array<String?> {
val info =
context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
val ps = info.requestedPermissions
if (ps != null && ps.isNotEmpty()) {
ps
} else {
arrayOfNulls(0)
}
return ps
}
private fun runtimePermissions(context: Context) {
val allNeededPermissions: MutableList<String?> = ArrayList()
for (permission in requiredPermissions(context)) {
if (!isPermissionGranted(context, permission)) {
allNeededPermissions.add(permission)
}
}
if (allNeededPermissions.isNotEmpty()) {
ActivityCompat.requestPermissions(
getActivity(context),
allNeededPermissions.toTypedArray(),
PERMISSION_REQUESTS
)
}
}
@SuppressLint("UseRequireInsteadOfGet")
private fun getActivity(context: Context) = when (context) {
is Fragment -> {
(context as Fragment).activity!!
}
is Activity -> {
context
}
else -> {
throw RuntimeException("context 必须是 Activity 或者 Fragment ")
}
}
}
运行起来看下效果
ok,没问题
有问题请留言!