当前位置:   article > 正文

Android CameraX 使用教程

android camerax

Google 原来提供了Camera,后面又推出了Camera2,然后几年前在Jetpack里面推出了CameraX

CameraX其实内部是封装Camera2的。但是它比Camera2使用起来更加简便,性能也比Camera2提高了。

废话少说,直接上代码:

要用CameraX,第一是要引入其 dependencies

  1. // CameraX core library
  2. def camerax_version = '1.1.0-beta01'
  3. implementation "androidx.camera:camera-core:$camerax_version"
  4. // CameraX Camera2 extensions
  5. implementation "androidx.camera:camera-camera2:$camerax_version"
  6. // CameraX Lifecycle library
  7. implementation "androidx.camera:camera-lifecycle:$camerax_version"
  8. // CameraX View class
  9. implementation "androidx.camera:camera-view:$camerax_version"

进而上个布局;

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".CameraActivity">
  8. <!--预览的控件-->
  9. <androidx.camera.view.PreviewView
  10. android:id="@+id/preview_view"
  11. android:layout_width="0dp"
  12. android:layout_height="0dp"
  13. app:scaleType="fillCenter"
  14. app:layout_constraintBottom_toBottomOf="parent"
  15. app:layout_constraintLeft_toLeftOf="parent"
  16. app:layout_constraintRight_toRightOf="parent"
  17. app:layout_constraintTop_toTopOf="parent" />
  18. <androidx.appcompat.widget.AppCompatButton
  19. android:id="@+id/switchCamera"
  20. android:layout_width="100dp"
  21. android:layout_height="50dp"
  22. android:layout_marginTop="10dp"
  23. android:text="切换相机"
  24. app:layout_constraintLeft_toLeftOf="parent"
  25. app:layout_constraintRight_toRightOf="parent"
  26. app:layout_constraintTop_toTopOf="parent">
  27. </androidx.appcompat.widget.AppCompatButton>
  28. <androidx.appcompat.widget.AppCompatTextView
  29. android:id="@+id/tvFps"
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. app:layout_constraintTop_toBottomOf="@id/switchCamera"
  33. app:layout_constraintLeft_toLeftOf="parent"
  34. android:textSize="20sp"
  35. android:gravity="center"
  36. android:padding="5dp"
  37. android:text="12456"
  38. android:textColor="@color/white">
  39. </androidx.appcompat.widget.AppCompatTextView>
  40. </androidx.constraintlayout.widget.ConstraintLayout>

然后上个Activity代码:

  1. import androidx.appcompat.app.AppCompatActivity
  2. import android.os.Bundle
  3. import android.os.SystemClock
  4. import android.util.Log
  5. import android.view.View
  6. import androidx.appcompat.widget.AppCompatTextView
  7. import androidx.camera.core.*
  8. import androidx.camera.lifecycle.ProcessCameraProvider
  9. import androidx.camera.view.PreviewView
  10. import androidx.core.content.ContextCompat
  11. class CameraActivity : AppCompatActivity() {
  12. companion object{
  13. private const val TAG = "CameraActivity"
  14. }
  15. private var cameraProvider: ProcessCameraProvider? = null
  16. private var previewUseCase: Preview? = null
  17. private lateinit var previewView: PreviewView
  18. private lateinit var tvFps: AppCompatTextView
  19. private var cameraSelector: CameraSelector? = null
  20. private var lensFacing = CameraSelector.LENS_FACING_FRONT
  21. private var camera: Camera? = null
  22. private var analysisUseCase: ImageAnalysis? = null
  23. override fun onCreate(savedInstanceState: Bundle?) {
  24. super.onCreate(savedInstanceState)
  25. setContentView(R.layout.activity_camera)
  26. previewView = findViewById(R.id.preview_view)
  27. tvFps = findViewById(R.id.tvFps)
  28. findViewById<View>(R.id.switchCamera).setOnClickListener {
  29. switchCamera()
  30. }
  31. cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
  32. if (PermissionsUtil.allPermissionsGranted(this)){
  33. start()
  34. }else{
  35. PermissionsUtil.requestPermission(this)
  36. }
  37. }
  38. private fun start() {
  39. /*
  40. 通过 ProcessCameraProvider 获取 cameraProvider
  41. cameraProvider 就是我们持有的相机实例
  42. */
  43. val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
  44. cameraProviderFuture.addListener(Runnable {
  45. cameraProvider = cameraProviderFuture.get()
  46. startCamera()
  47. }, ContextCompat.getMainExecutor(this))
  48. }
  49. override fun onRequestPermissionsResult(
  50. requestCode: Int,
  51. permissions: Array<out String>,
  52. grantResults: IntArray
  53. ) {
  54. super.onRequestPermissionsResult(requestCode, permissions, grantResults)
  55. if (PermissionsUtil.allPermissionsGranted(this)){
  56. start()
  57. }
  58. }
  59. /**
  60. * 启动相机
  61. * */
  62. private fun startCamera() {
  63. if (cameraProvider != null) {
  64. cameraProvider!!.unbindAll()
  65. /*
  66. 这一步是绑定预览界面,如果不需要预览界面,这一步克注释掉
  67. CameraX优势体验之一:预览界面可以根据开发者需求去取舍,而Camera1和Camera2则必须要预览界面
  68. */
  69. bindPreviewUseCase()
  70. // 这一步是绑定相机预览数据,可以获得相机每一帧的数据
  71. bindAnalysisUseCase()
  72. }
  73. }
  74. /**
  75. * 绑定预览界面。不需要预览界面可以不调用
  76. * */
  77. private fun bindPreviewUseCase() {
  78. if (previewUseCase != null) {
  79. cameraProvider!!.unbind(previewUseCase)
  80. }
  81. val builder = Preview.Builder()
  82. builder.setTargetAspectRatio(AspectRatio.RATIO_16_9).build()
  83. previewUseCase = builder.build()
  84. previewUseCase!!.setSurfaceProvider(previewView.surfaceProvider)
  85. cameraProvider!!.bindToLifecycle(
  86. this,
  87. cameraSelector!!,
  88. previewUseCase
  89. )
  90. }
  91. private var lastTime = 0L
  92. private var lastShowTime = 0L
  93. private var count = 0
  94. private var maxFrameMs = 0L
  95. private var minFrameMs = Long.MAX_VALUE
  96. private fun bindAnalysisUseCase() {
  97. if (analysisUseCase != null) {
  98. cameraProvider!!.unbind(analysisUseCase)
  99. }
  100. val builder = ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9)
  101. analysisUseCase = builder.build()
  102. analysisUseCase?.setAnalyzer(
  103. ContextCompat.getMainExecutor(this)
  104. ) { imageProxy: ImageProxy ->
  105. count += 1
  106. val currentTime = SystemClock.elapsedRealtime()
  107. val d = currentTime - lastShowTime
  108. maxFrameMs = maxFrameMs.coerceAtLeast(d)
  109. minFrameMs = minFrameMs.coerceAtMost(d)
  110. if ((currentTime - lastTime) >= 1000){
  111. tvFps.text = "fps: ${count}, 两帧最大:${maxFrameMs}, 最小:${minFrameMs}"
  112. lastTime = currentTime
  113. count = 0
  114. maxFrameMs = 0
  115. minFrameMs = Long.MAX_VALUE
  116. }
  117. lastShowTime = currentTime
  118. Log.d(TAG,"ImageProxy: 宽高: ${imageProxy.width} * ${imageProxy.height}")
  119. //必须close,相机才会下发下一帧数据,否则会一直阻塞相机下发数据
  120. imageProxy.close()
  121. }
  122. camera = cameraProvider!!.bindToLifecycle(
  123. this,
  124. cameraSelector!!,
  125. analysisUseCase
  126. )
  127. }
  128. private fun switchCamera() {
  129. if (cameraProvider == null) {
  130. return
  131. }
  132. val newLensFacing =
  133. if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
  134. CameraSelector.LENS_FACING_BACK
  135. } else {
  136. CameraSelector.LENS_FACING_FRONT
  137. }
  138. val newCameraSelector = CameraSelector.Builder().requireLensFacing(newLensFacing).build()
  139. try {
  140. if (cameraProvider!!.hasCamera(newCameraSelector)) {
  141. lensFacing = newLensFacing
  142. cameraSelector = newCameraSelector
  143. startCamera()
  144. return
  145. }
  146. } catch (e: CameraInfoUnavailableException) {
  147. // Falls through
  148. }
  149. }
  150. }

添加权限工具类代码

  1. object PermissionsUtil {
  2. private const val TAG = "PermissionsUtil"
  3. private const val PERMISSION_REQUESTS = 1
  4. fun requestPermission(context: Context) {
  5. if (!allPermissionsGranted(context)) {
  6. runtimePermissions(context)
  7. }
  8. }
  9. private fun isPermissionGranted(context: Context, permission: String?): Boolean {
  10. if (ContextCompat.checkSelfPermission(context, permission!!) ==
  11. PackageManager.PERMISSION_GRANTED
  12. ) {
  13. return true
  14. }
  15. return false
  16. }
  17. fun allPermissionsGranted(context: Context): Boolean {
  18. for (permission in requiredPermissions(context)) {
  19. if (!isPermissionGranted(context, permission)) {
  20. return false
  21. }
  22. }
  23. return true
  24. }
  25. private fun requiredPermissions(context: Context): Array<String?> {
  26. val info =
  27. context.packageManager.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
  28. val ps = info.requestedPermissions
  29. if (ps != null && ps.isNotEmpty()) {
  30. ps
  31. } else {
  32. arrayOfNulls(0)
  33. }
  34. return ps
  35. }
  36. private fun runtimePermissions(context: Context) {
  37. val allNeededPermissions: MutableList<String?> = ArrayList()
  38. for (permission in requiredPermissions(context)) {
  39. if (!isPermissionGranted(context, permission)) {
  40. allNeededPermissions.add(permission)
  41. }
  42. }
  43. if (allNeededPermissions.isNotEmpty()) {
  44. ActivityCompat.requestPermissions(
  45. getActivity(context),
  46. allNeededPermissions.toTypedArray(),
  47. PERMISSION_REQUESTS
  48. )
  49. }
  50. }
  51. @SuppressLint("UseRequireInsteadOfGet")
  52. private fun getActivity(context: Context) = when (context) {
  53. is Fragment -> {
  54. (context as Fragment).activity!!
  55. }
  56. is Activity -> {
  57. context
  58. }
  59. else -> {
  60. throw RuntimeException("context 必须是 Activity 或者 Fragment ")
  61. }
  62. }
  63. }

运行起来看下效果

ok,没问题

有问题请留言!

demo地址 https://github.com/LeoLiang23/cameraDemo

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/461490
推荐阅读
相关标签
  

闽ICP备14008679号