赞
踩
本文将初步介绍jetpeck库之camerax的初步基本使用,包括实现取景器、拍摄照片、录制视频三个功能用例。
话不多说,直接上代码。
- def camerax_version = "1.1.0-beta01"
- implementation "androidx.camera:camera-core:${camerax_version}"
- implementation "androidx.camera:camera-camera2:${camerax_version}"
- implementation "androidx.camera:camera-lifecycle:${camerax_version}"
- implementation "androidx.camera:camera-video:${camerax_version}"
-
- implementation "androidx.camera:camera-view:${camerax_version}"
- implementation "androidx.camera:camera-extensions:${camerax_version}"
- android {
- compileSdkVersion 31
-
- defaultConfig {
- applicationId "composer.kotlin"
- ...
- }
- ...
- buildFeatures{
- // 配置开启viewbinding
- viewBinding = true
- }
- }
- import android.Manifest
- import android.content.ContentValues
- import android.content.pm.PackageManager
- import android.os.Build
- import android.os.Bundle
- import android.provider.MediaStore
- 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.video.*
- import androidx.camera.video.VideoCapture
- import androidx.core.app.ActivityCompat
- import androidx.core.content.ContextCompat
- import androidx.core.content.PermissionChecker
- import composer.R
- import composer.databinding.ActivityCameraxBinding
- import kotlinx.android.synthetic.main.activity_camerax.*
- import java.nio.ByteBuffer
- import java.text.SimpleDateFormat
- import java.util.*
- import java.util.concurrent.ExecutorService
- import java.util.concurrent.Executors
-
- typealias LumaListener = (luma: Double) -> Unit
-
- class CameraxActivity : AppCompatActivity() {
- private lateinit var viewBinding: ActivityCameraxBinding
-
- private var imageCapture: ImageCapture? = null
-
- private var videoCapture: VideoCapture<Recorder>? = null
- private var recording: Recording? = null
-
- private lateinit var cameraExecutor: ExecutorService
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- viewBinding = ActivityCameraxBinding.inflate(layoutInflater)
- setContentView(viewBinding.root)
-
- if (allPermissionsGranted()) {
- startCamera()
- } else {
- ActivityCompat.requestPermissions(
- this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
- }
-
- viewBinding.imageCaptureButton.setOnClickListener { takePhoto() }
- viewBinding.videoCaptureButton.setOnClickListener { captureVideo() }
-
- cameraExecutor = Executors.newSingleThreadExecutor()
- }
-
- /**
- * 捕获图片
- */
- private fun takePhoto() {
- val imageCapture = imageCapture ?: return
-
- val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
- .format(System.currentTimeMillis())
- val contentValues = ContentValues().apply {
- put(MediaStore.MediaColumns.DISPLAY_NAME, name)
- put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
- if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
- put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
- }
- }
-
- val outputOptions = ImageCapture.OutputFileOptions
- .Builder(contentResolver,
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- contentValues)
- .build()
-
- 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 msg = "Photo capture succeeded: ${output.savedUri}"
- Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
- Log.d(TAG, msg)
- }
- }
- )
- }
-
- /**
- * 捕获视频
- */
- private fun captureVideo() {
- val videoCapture = this.videoCapture ?: return
-
- viewBinding.videoCaptureButton.isEnabled = false
-
- val curRecording = recording
- if (curRecording != null) {
- // 停止当前录制
- curRecording.stop()
- recording = null
- return
- }
-
- val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US)
- .format(System.currentTimeMillis())
- val contentValues = ContentValues().apply {
- put(MediaStore.MediaColumns.DISPLAY_NAME, name)
- put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
- put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video")
- }
- }
-
- val mediaStoreOutputOptions = MediaStoreOutputOptions
- .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
- .setContentValues(contentValues)
- .build()
- recording = videoCapture.output
- .prepareRecording(this, mediaStoreOutputOptions)
- .apply {
- if (PermissionChecker.checkSelfPermission(this@CameraxActivity,
- Manifest.permission.RECORD_AUDIO) ==
- PermissionChecker.PERMISSION_GRANTED)
- {
- withAudioEnabled()
- }
- }
- .start(ContextCompat.getMainExecutor(this)) { recordEvent ->
- when(recordEvent) {
- is VideoRecordEvent.Start -> {
- viewBinding.videoCaptureButton.apply {
- text = getString(R.string.stop_capture)
- isEnabled = true
- }
- }
- is VideoRecordEvent.Finalize -> {
- if (!recordEvent.hasError()) {
- val msg = "Video capture succeeded: " +
- "${recordEvent.outputResults.outputUri}"
- Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT)
- .show()
- Log.d(TAG, msg)
- } else {
- recording?.close()
- recording = null
- Log.e(TAG, "Video capture ends with error: " +
- "${recordEvent.error}")
- }
- viewBinding.videoCaptureButton.apply {
- text = getString(R.string.start_capture)
- isEnabled = true
- }
- }
- }
- }
- }
-
- /**
- * 启动相机,开机相机取景预览
- */
- private fun startCamera() {
- val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
-
- cameraProviderFuture.addListener({
- val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
-
- // 开启相机取景预览
- val preview = Preview.Builder()
- .build()
- .also {
- it.setSurfaceProvider(viewFinder.surfaceProvider)
- }
-
- imageCapture = ImageCapture.Builder()
- .build()
-
- val recorder = Recorder.Builder()
- .setQualitySelector(QualitySelector.from(Quality.HIGHEST))
- .build()
- videoCapture = VideoCapture.withOutput(recorder)
-
- val imageAnalyzer = ImageAnalysis.Builder()
- .build()
- .also {
- it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
- Log.d(TAG, "Average luminosity: $luma")
- })
- }
-
- // 默认使用后置摄像头
- val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
-
- try {
- cameraProvider.unbindAll()
-
- // 混合使用preview、imageCapture、和videoCapture三个用例
- cameraProvider.bindToLifecycle(
- this, cameraSelector, preview, imageCapture, videoCapture)
-
- // 注意:不支持 preview + imageCapture + videoCapture + imageAnalysis 组合
- // cameraProvider.bindToLifecycle(
- // this, cameraSelector, preview, imageAnalyzer, imageCapture, videoCapture)
- } 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
- }
-
- override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (requestCode == REQUEST_CODE_PERMISSIONS) {
- if (allPermissionsGranted()) {
- startCamera()
- } else {
- Toast.makeText(this,
- "Permissions not granted by the user.",
- Toast.LENGTH_SHORT).show()
- finish()
- }
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- cameraExecutor.shutdown()
- }
-
- companion object {
- private const val TAG = "baorant"
- private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
- private const val REQUEST_CODE_PERMISSIONS = 10
- private val REQUIRED_PERMISSIONS =
- mutableListOf (
- Manifest.permission.CAMERA,
- Manifest.permission.RECORD_AUDIO
- ).apply {
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
- add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- }
- }.toTypedArray()
- }
-
- private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {
-
- private fun ByteBuffer.toByteArray(): ByteArray {
- rewind()
- val data = ByteArray(remaining())
- get(data)
- return data
- }
-
- 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()
- }
- }
- }
- <?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=".view.CameraxActivity">
-
- <androidx.camera.view.PreviewView
- android:id="@+id/viewFinder"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <Button
- android:id="@+id/image_capture_button"
- android:layout_width="110dp"
- android:layout_height="110dp"
- android:layout_marginBottom="50dp"
- android:layout_marginEnd="50dp"
- android:elevation="2dp"
- android:text="@string/take_photo"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintLeft_toLeftOf="parent"
- app:layout_constraintEnd_toStartOf="@id/vertical_centerline" />
-
- <Button
- android:id="@+id/video_capture_button"
- android:layout_width="110dp"
- android:layout_height="110dp"
- android:layout_marginBottom="50dp"
- android:layout_marginStart="50dp"
- android:elevation="2dp"
- android:text="@string/start_capture"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@id/vertical_centerline" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/vertical_centerline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintGuide_percent=".50" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
- <string name="take_photo">拍照</string>
- <string name="start_capture">开始捕捉</string>
- <string name="stop_capture">停止捕获</string>
实现取景器功能,点击拍照功能可以捕捉图像存入手机本地。 点击开始捕获按钮开始视频捕获,停止后视频保存到本地。
本文初步介绍了jetpeck库之camerax的初步基本使用,包括实现取景器、拍摄照片、录制视频三个功能用例,有兴趣的同学可以进一步深入研究。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。