Intents y Estados en ViewModel para Cámara en Android
Clase 25 de 33 • Curso de Android: Integración de APIs nativas
Resumen
Trabajar de manera efectiva con la lógica de una pantalla de cámara en Android requiere definir claramente intents y estados de la interfaz de usuario (UI). En este contenido, aprenderemos cómo crear y manipular estos elementos esenciales usando Kotlin y su ViewModel, facilitando acciones como la toma de fotos, solicitud de permisos y visualización previa.
¿Qué son los intents y por qué son importantes en nuestra app de cámara?
Los intents son acciones específicas que informan a nuestra aplicación qué tarea debe ejecutarse, en este caso relacionadas con la cámara del dispositivo. Para organizarlos, utilizamos sealed interface
en Kotlin. Los principales intents a considerar son:
- FotoTomada: nos avisa cuando se ha tomado una foto y contiene su información en un
ByteArray
. El uso de las funcionesEquals
yHashCode
generadas por Kotlin permite identificar si dos fotos son exactamente la misma imagen. - SubmitCameraPermissionInfo: indica si el permiso de acceso a la cámara fue aceptado por el usuario y si la app debe explicar el porqué (rationale). Esto se apunta a través de valores booleanos:
AcceptedCameraPermission
: aceptación del permiso por el usuario.shouldShowCameraRationally
: necesidad de mostrar una justificación clara al usuario antes de pedir permiso.- DataObjects adicionales:
- FotoGuardada: indica que el usuario aceptó y guardó la foto.
- CancelarFoto: describe la acción de cancelar la visualización previa o la captura de la foto.
¿Cómo crear y administrar el estado de la interfaz de usuario?
Gestionar el estado de nuestra UI implica definir claramente en qué condición se encuentra la interfaz en cualquier momento. Definimos CameraUIState
utilizando una DataClass
, que incluye:
isInPreviewMode
: estado que define si la pantalla actualmente muestra una vista previa después de tomar una foto.LastSavedPhoto
: referencia al archivo que representa la última foto guardada.ShowCameraRationales
: booleano que indica si debemos mostrar racionales de permisos al usuario.
Iniciamos estos valores con condiciones por defecto útiles:
isInPreviewMode
: 0 (la cámara está lista para tomar una foto).ShowCameraRationales
: false (por defecto no mostramos racionales).
¿De qué manera implementar el ViewModel utilizando Hilt y por qué hacerlo así?
El ViewModel es un componente esencial en Android que mantiene y gestiona la lógica de la interfaz. Usamos Hilt para facilitar la inyección de dependencias, beneficiándonos así de un mejor manejo de la memoria y estructura del código. En nuestro ejemplo, el ViewModel llamado CameraViewModel
tiene las siguientes características clave:
- Se anota con
@HiltViewModel
y extiende del propioViewModel
. - Se inyectan mediante constructor dos componentes esenciales:
- PhotoHandler: maneja la obtención y guardado de fotos.
- LocationTracker: proporciona la localización asociada a cada foto capturada.
Dentro del ViewModel también:
- Definimos un estado mutable expuesto mediante un
MutableStateFlow
y asStateFlow para la UI. - Exponemos la foto de vista previa
PreviewPhoto
en la interfaz mediante unStateFlow
, usando el operadorstateIn
, que retiene el valor durante cinco segundos tras la cancelación de la suscripción.
¿Cómo gestionar los intents en nuestro ViewModel?
Para administrar acciones derivadas de los intents definidos, usamos una función que maneja estos eventos mediante la estructura when
:
fun onIntentReceived(intent: CameraIntent) = when(intent) {
is CameraIntent.SubmitCameraPermissionInfo -> {
uiState.value = uiState.value.copy(
showCameraRationales = intent.shouldShowCameraRationally,
permissionGranted = intent.acceptedCameraPermission
)
}
// Aquí añadimos más acciones según los intents disponibles
}
Esta estructura permite reaccionar fácilmente a eventos, modificando adecuadamente el estado de la interfaz según corresponda.
¿Tienes alguna pregunta específica sobre estos componentes o cómo usarlos en tus proyectos Android? ¡Déjanos tu comentario y continúa aprendiendo!