Cómo implementar una vista previa

Cuando agregues una vista previa a tu app, usa PreviewView, que es una View que se puede recortar, escalar y rotar para mostrarse correctamente.

La vista previa de la imagen se transmite a una superficie dentro de PreviewView cuando se activa la cámara.

Cómo usar PreviewView

La implementación de una vista previa de CameraX con PreviewView implica los siguientes pasos, que se explican en secciones posteriores:

  1. De manera opcional, configura un CameraXConfig.Provider.
  2. Agrega un elemento PreviewView a tu diseño.
  3. Solicita un elemento ProcessCameraProvider.
  4. En la creación de View, comprueba la presencia de ProcessCameraProvider.
  5. Selecciona una cámara y vincula el ciclo de vida y los casos de uso.

El uso de PreviewView tiene algunas limitaciones. Cuando usas PreviewView, no puedes hacer nada de lo siguiente:

  • Crear un elemento SurfaceTexture para configurar TextureView y Preview.SurfaceProvider
  • Recuperar el elemento SurfaceTexture de TextureView y configurarlo en Preview.SurfaceProvider
  • Obtener el elemento Surface de SurfaceView y configurarlo en Preview.SurfaceProvider

Si ocurre algo de lo anterior, Preview deja de transmitir fotogramas a PreviewView.

Cómo agregar un elemento PreviewView a tu diseño

En el siguiente ejemplo, se muestra un PreviewView en un diseño:

<FrameLayout
    android:id="@+id/container">
        <androidx.camera.view.PreviewView
            android:id="@+id/previewView" />
</FrameLayout>

Cómo solicitar un elemento CameraProvider

En el siguiente código, se muestra cómo solicitar un CameraProvider:

Kotlin

import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.common.util.concurrent.ListenableFuture

class MainActivity : AppCompatActivity() {
    private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider>
    override fun onCreate(savedInstanceState: Bundle?) {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    }
}

Java

import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.common.util.concurrent.ListenableFuture

public class MainActivity extends AppCompatActivity {
    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    }
}

Comprueba la disponibilidad de CameraProvider

Después de solicitar un elemento CameraProvider, verifica que la inicialización se haya realizado correctamente cuando se cree la vista. El siguiente código muestra cómo hacer esto:

Kotlin

cameraProviderFuture.addListener(Runnable {
    val cameraProvider = cameraProviderFuture.get()
    bindPreview(cameraProvider)
}, ContextCompat.getMainExecutor(this))

Java

cameraProviderFuture.addListener(() -> {
    try {
        ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
        bindPreview(cameraProvider);
    } catch (ExecutionException | InterruptedException e) {
        // No errors need to be handled for this Future.
        // This should never be reached.
    }
}, ContextCompat.getMainExecutor(this));

Para ver un ejemplo de la función bindPreview que se usa en esta muestra, consulta el código proporcionado en la siguiente sección.

Selecciona una cámara y vincula el ciclo de vida y los casos de uso

Una vez que hayas creado y confirmado el CameraProvider, haz lo siguiente:

  1. Crea un elemento Preview.
  2. Especifica la opción de LensFacing deseada para la cámara.
  3. Vincula la cámara y los casos de uso seleccionados al ciclo de vida.
  4. Conecta Preview a PreviewView.

En el siguiente código, se muestra un ejemplo:

Kotlin

fun bindPreview(cameraProvider : ProcessCameraProvider) {
    var preview : Preview = Preview.Builder()
            .build()

    var cameraSelector : CameraSelector = CameraSelector.Builder()
          .requireLensFacing(CameraSelector.LENS_FACING_BACK)
          .build()

    preview.setSurfaceProvider(previewView.getSurfaceProvider())

    var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)
}

Java

void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
    Preview preview = new Preview.Builder()
            .build();

    CameraSelector cameraSelector = new CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build();

    preview.setSurfaceProvider(previewView.getSurfaceProvider());

    Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, preview);
}

Ten en cuenta que bindToLifecycle() devuelve un objeto Camera. Si quieres obtener más información para controlar la salida de la cámara, como el zoom y la exposición, consulta Salida de cámara.

Ya terminaste de implementar la vista previa de la cámara. Compila tu app y confirma que la vista previa aparezca en ella y funcione como lo deseas.

Controles adicionales para PreviewView

CameraX PreviewView proporciona algunas API adicionales para configurar propiedades como las siguientes:

Modo de implementación

PreviewView puede usar uno de los siguientes modos para renderizar una transmisión de vista previa en el View de destino:

  • PERFORMANCE es el modo predeterminado. PreviewView usa un SurfaceView para mostrar la transmisión de video por Internet, pero recurre a un TextureView en casos determinados. SurfaceView posee una superficie de dibujo dedicada, que tiene más probabilidades de que el compositor de hardware interno lo implemente con una superposición de hardware, en especial cuando no hay otros elementos de la IU (botones "Me gusta") en la parte superior del video de vista previa. Al aplicar renderización con una superposición de hardware, los fotogramas de video evitan una ruta de GPU, lo que puede reducir el consumo de energía y la latencia de la plataforma.

  • Modo COMPATIBLE. En este modo, PreviewView usa un TextureView que, a diferencia de SurfaceView, no tiene una superficie de dibujo dedicada. Como resultado, el video se renderiza con una compaginación para que se pueda mostrar. Durante este paso adicional, la aplicación puede realizar otras tareas de procesamiento, como las de escalar y rotar videos sin restricciones.

Usa PreviewView.setImplementationMode() para seleccionar el modo de implementación adecuado para tu aplicación. Si el modo PERFORMANCE predeterminado no es adecuado para tu aplicación, en la siguiente muestra de código se muestra cómo configurar el modo COMPATIBLE:

Kotlin

// viewFinder is a PreviewView instance
viewFinder.implementationMode = PreviewView.ImplementationMode.COMPATIBLE

Tipo de escala

Cuando la resolución de video de vista previa difiere de las dimensiones de tu PreviewView de destino, el contenido de video debe ajustarse a la vista recortando o usando formato letterbox (conservando la relación de aspecto original). PreviewView proporciona lo siguiente a ScaleTypes para este fin:

  • FIT_CENTER, FIT_START y FIT_END para el formato letterbox. El contenido de video completo se ajusta (aumenta o reduce) al tamaño máximo posible que se puede mostrar en el PreviewView de destino. Sin embargo, aunque el fotograma del video completo es visible, es posible que parte de la pantalla esté en blanco. Según cuál de estos tres tipos de escala elijas, el marco de video se alineará con el centro, el inicio o el final de la vista de destino.

  • FILL_CENTER, FILL_START y FILL_END para recorte. Si un video no coincide con la relación de aspecto de PreviewView, solo se verá una parte del contenido, pero llenará todo el PreviewView.

El tipo de escala predeterminado que usa CameraX es FILL_CENTER. Usa PreviewView.setScaleType() para establecer el tipo de escala más apropiado para tu aplicación. En la siguiente muestra de código, se configura el tipo de escala FIT_CENTER:

Kotlin

// viewFinder is a PreviewView instance
viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER

El proceso para mostrar un video consta de los siguientes pasos:

  1. Escala el video:
    • Para los tipos de escala FIT_*, escala el video con min(dst.width/src.width, dst.height/src.height).
    • Para los tipos de escala FILL_*, escala el video con max(dst.width/src.width, dst.height/src.height).
  2. Alinea el video ajustado con el destino PreviewView:
    • Para FIT_CENTER/FILL_CENTER, alinea el centro del video ajustado a escala con el PreviewView de destino.
    • Para FIT_START/FILL_START, alinea el video ajustado y el PreviewView de destino con respecto a la esquina superior izquierda de cada uno.
    • Para FIT_END/FILL_END, alinea el video ajustado y el destino PreviewView con respecto a la esquina inferior derecha de cada uno.

Por ejemplo, aquí hay un video de origen de 640 x 480 y un destino de PreviewView de 1920 x 1080:

Imagen de un video de 640 x 480 en comparación con una vista previa de 1920 x 1080

En la siguiente imagen, se muestra el proceso de escalamiento de FIT_START, FIT_CENTER y FIT_END:

Imagen del proceso de escalamiento de FIT_START, FIT_CENTER y FIT_END

El proceso funciona de la siguiente manera:

  1. Escala el marco de video (manteniendo la relación de aspecto original) con min(1920/640, 1080/480) = 2.25 para obtener un marco de video intermedio de 1440 x 1080.
  2. Alinea el marco de video de 1440 x 1080 con el PreviewView de 1920 x 1080.
    • Para FIT_CENTER, alinea el marco de video con el centro de la ventana PreviewView. Las columnas de 240 píxeles inicial y final de PreviewView están en blanco.
    • Para FIT_START, alinea el marco de video con el inicio (esquina superior izquierda) de la ventana PreviewView. Las columnas finales de 480 píxeles de PreviewView están en blanco.
    • Para FIT_END, alinea el fotograma del video con el extremo (esquina inferior derecha) de la ventana PreviewView. Las columnas iniciales de 480 píxeles de PreviewView están en blanco.

En la siguiente imagen, se muestra el proceso de escalamiento de FILL_START, FILL_CENTER y FILL_END:

Imagen del proceso de escalamiento de FILL_START, FILL_CENTER y FILL_END

El proceso funciona de la siguiente manera:

  1. Escala el marco de video con max(1920/640, 1080/480) = 3 para obtener un marco de video intermedio de 1920 x 1440 (que es más grande que PreviewView).
  2. Recorta el marco de video de 1920 x 1440 para que se ajuste a la ventana PreviewView de 1920 x 1080.
    • Para FILL_CENTER, recorta 1920 x 1080 del centro del video escalado de 1920 x 1440. Las 180 líneas de video superiores e inferiores no son visibles.
    • Para FILL_START, recorta 1920 x 1080 desde el inicio del video ajustado a 1920 x 1440. Las 360 líneas de video inferiores no son visibles.
    • Para FILL_END, recorta 1920 x 1080 desde el extremo del video escalado de 1920 x 1440. Las principales 360 líneas de video no son visibles.

Recursos adicionales

Para obtener más información sobre CameraX, consulta los siguientes recursos adicionales:

Codelab

  • Cómo comenzar a usar CameraX
  • Muestra de código

  • Apps de ejemplo de CameraX