如何在安卓系统中使用 CameraX 创建自定义摄像头?
原文:https://www . geeksforgeeks . org/如何使用 android 中的 camerax 创建自定义相机/
CameraX 用于在 app 中创建自定义相机。 CameraX 是一个 Jetpack 支持库,旨在帮助您简化相机应用程序开发。下面给出了一个示例视频,以了解我们将在本文中做什么。注意,我们将使用柯特林语言来实现这个项目。
分步实施
第一步:创建新项目
要在安卓工作室创建新项目,请参考如何在安卓工作室创建/启动新项目。请注意,选择 Kotlin 作为编程语言。
第 2 步:向 build.gradle 文件添加依赖项,然后单击“立即同步”
def camera _ version = " 1 . 0 . 0-beta 07 "
//使用 camera2 实现的 CameraX 核心库
实现“androidx . camera:camera-camera 2:$ camera x _ version”
// CameraX 生命周期库
实现“androidx . camera:camera-life cycle:$ camera x _ version”
// CameraX 视图类
实现“androidx . camera:camera-view:1 . 0 . 0-alpha 14”
如果 kotlin 扩展缺失,如下图添加kot Lin-安卓-扩展,点击“立即同步”。
插件{
id 'com.android.application '
kot Lin Android id
id 'kotlin-android-extensions '
}
第三步:添加摄像头权限
转到 AndroidManifest.xml 添加摄像头权限。
第 4 步:使用 activity_main.xml 文件
转到 activity_main.xml 文件,参考以下代码。下面是 activity_main.xml 文件的代码。请注意,有一个名为预览视图的视图带有 id 取景器,我们将把它用作相机的取景器。
可扩展标记语言
<?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"
android:background="@color/black"
tools:context=".MainActivity">
<Button
android:id="@+id/camera_capture_button"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_marginBottom="50dp"
android:background="@drawable/capture_button"
android:elevation="2dp"
android:scaleType="fitCenter"
android:text="Capture Photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<ImageView
android:id="@+id/iv_capture"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="50dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="450dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
第 5 步:创建可绘制的捕捉按钮
转到 res >可绘制并创建一个新文件 capture_button.xml 。下面是它的代码。
可扩展标记语言
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#0F9D58" />
<size
android:width="118dp"
android:height="118dp" />
</shape>
第 6 步:使用 MainActivity.kt 文件
转到 MainActivity.kt 文件,参考以下代码。下面是 MainActivity.kt 文件的代码。代码中添加了注释,以更详细地理解代码。
我的锅
import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private var imageCapture: ImageCapture? = null
private lateinit var outputDirectory: File
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// hide the action bar
supportActionBar?.hide()
// Check camera permissions if all permission granted
// start camera else ask for the permission
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
// set on click listener for the button of capture photo
// it calls a method which is implemented below
findViewById<Button>(R.id.camera_capture_button).setOnClickListener {
takePhoto()
}
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor()
}
private fun takePhoto() {
// Get a stable reference of the
// modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".jpg"
)
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Set up 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)
// set the saved uri to the image view
findViewById<ImageView>(R.id.iv_capture).visibility = View.VISIBLE
findViewById<ImageView>(R.id.iv_capture).setImageURI(savedUri)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_LONG).show()
Log.d(TAG, msg)
}
})
}
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
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.createSurfaceProvider())
}
imageCapture = ImageCapture.Builder().build()
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture
)
} catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc)
}
}, ContextCompat.getMainExecutor(this))
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
// creates a folder inside internal storage
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
}
// checks the camera permission
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
// If all permissions granted , then start Camera
if (allPermissionsGranted()) {
startCamera()
} else {
// If permissions are not granted,
// present a toast to notify the user that
// the permissions were not granted.
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
finish()
}
}
}
companion object {
private const val TAG = "CameraXGFG"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 20
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
}