¿Cómo crear una cámara personalizada usando CameraX en Android?

CameraX se utiliza para crear una cámara personalizada en la aplicación. CameraX es una biblioteca de soporte de Jetpack , creada para ayudarlo a facilitar el desarrollo de aplicaciones de cámara. A continuación se muestra un video de muestra para tener una idea de lo que vamos a hacer en este artículo. Tenga en cuenta que vamos a implementar este proyecto utilizando el lenguaje Kotlin .

Implementación paso a paso

Paso 1: Crear un nuevo proyecto

Para crear un nuevo proyecto en Android Studio, consulte Cómo crear/iniciar un nuevo proyecto en Android Studio . Tenga en cuenta que seleccione Kotlin como lenguaje de programación.

Paso 2: agregue la dependencia al archivo build.gradle y haga clic en «sincronizar ahora»

def camerax_version = “1.0.0-beta07”

// Biblioteca central de CameraX usando la implementación de camera2

implementación “androidx.camera:camera-camera2:$camerax_version”

// Biblioteca del ciclo de vida de CameraX

implementación “androidx.camera:camera-lifecycle:$camerax_version”

// CameraX Ver clase

implementación “androidx.camera:cámara-vista:1.0.0-alpha14”

Si falta la extensión kotlin, agregue kotlin-android-extensions como se muestra a continuación y haga clic en «Sincronizar ahora».

complementos {

   id ‘com.android.aplicación’

   id ‘kotlin-android’

   id ‘kotlin-android-extensiones’

}

Paso 3: Agregar permiso de cámara

Vaya a AndroidManifest.xml y agregue el permiso de la cámara.

<usos-función android:name=”android.hardware.camera.any” />

<usos-permiso android:name=”android.permission.CAMERA” />

Paso 4: trabajar con el archivo activity_main.xml

Vaya al archivo activity_main.xml y consulte el siguiente código. A continuación se muestra el código para el archivo activity_main.xml . Observe que hay una vista llamada PreviewView con id viewFinder que usaremos como visor para la cámara.

XML

<?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>

Paso 5: Crea un botón de captura dibujable

Ve a res > dibujable y crea un nuevo archivo capture_button.xml . A continuación se muestra el código de la misma.

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>

Paso 6: trabajar con el archivo MainActivity.kt

Vaya al archivo MainActivity.kt y consulte el siguiente código. A continuación se muestra el código del archivo MainActivity.kt . Se agregan comentarios dentro del código para comprender el código con más detalle.

Kotlin

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()
    }
}

Producción:

Publicación traducida automáticamente

Artículo escrito por introidx y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *