MVVM, LiveData, ViewBinding y RecyclerView (Actualizado 2025)¶
Este apartado profundiza en el uso moderno de MVVM, LiveData, ViewModel, View Binding y RecyclerView dentro del desarrollo Android con Kotlin + Jetpack (Compose y vistas tradicionales).
Concepto de MVVM¶
MVVM (Model – View – ViewModel) es un patrón de arquitectura que organiza la app en 3 capas:
- Model: datos, lógica de negocio y acceso a fuentes externas (BD, API, repositorios).
- View: interfaz de usuario (Activity, Fragment, Composable).
- ViewModel: prepara datos para la vista, expone estados observables y gestiona la lógica de UI.
Ejemplo simple de estructura MVVM¶
Model:
data class Tarea(val id: Int, val titulo: String, val completada: Boolean)
ViewModel:
class TareaViewModel : ViewModel() {
private val _tareas = MutableLiveData<List<Tarea>>()
val tareas: LiveData<List<Tarea>> = _tareas
fun cargarTareas() {
_tareas.value = listOf(
Tarea(1, "Estudiar Kotlin", false),
Tarea(2, "Preparar clase de PMDM", true)
)
}
}
Vista (Compose):
@Composable
fun TareasView(viewModel: TareaViewModel = viewModel()) {
val lista = viewModel.tareas.observeAsState(emptyList())
Column {
lista.value.forEach { tarea ->
Text("${tarea.titulo} - ${if (tarea.completada) "✔" else "✘"}")
}
}
}
Funcionamiento de MVVM paso a paso¶
Los pasos actualizados para Android serían:
- Crear un nuevo proyecto con Compose.
- Definir los modelos (data classes).
- Crear el ViewModel con
StateFlowoLiveData. - Implementar la vista en Compose o XML.
- Suscribir la vista al estado del ViewModel.
- Ejecutar la app.
Model (Capa de datos)¶
Ejemplo moderno con repositorio:
data class Producto(val id: Int, val nombre: String, val precio: Double)
class ProductoRepository {
fun obtenerProductos(): List<Producto> =
listOf(
Producto(1, "Monitor 27"", 199.99),
Producto(2, "Teclado Mecánico", 89.99)
)
}
View (Capa UI)¶
@Composable
fun ProductoItem(producto: Producto) {
Row {
Text(producto.nombre)
Spacer(Modifier.width(16.dp))
Text("${producto.precio} €")
}
}
ViewModel (Lógica de la vista)¶
Ejemplo actualizado con StateFlow:
class ProductoViewModel : ViewModel() {
private val repositorio = ProductoRepository()
private val _uiState = MutableStateFlow(emptyList<Producto>())
val uiState: StateFlow<List<Producto>> = _uiState
fun cargarProductos() {
_uiState.value = repositorio.obtenerProductos()
}
}
Vista observando StateFlow:
@Composable
fun ProductosScreen(viewModel: ProductoViewModel = viewModel()) {
val productos = viewModel.uiState.collectAsState()
Column {
productos.value.forEach { ProductoItem(it) }
}
}
Gradle (Dependencias)¶
Ejemplo de dependencias modernas en Kotlin DSL:
dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.0")
implementation("androidx.activity:activity-compose:1.10.0")
implementation("androidx.compose.ui:ui:1.7.0")
implementation("androidx.recyclerview:recyclerview:1.4.0")
}
LiveData¶
LiveData es un contenedor observable que respeta el ciclo de vida.
Ejemplo con LiveData¶
class ContadorViewModel : ViewModel() {
private val _contador = MutableLiveData(0)
val contador: LiveData<Int> = _contador
fun incrementar() {
_contador.value = (_contador.value ?: 0) + 1
}
}
Vista (Compose):
@Composable
fun ContadorScreen(vm: ContadorViewModel = viewModel()) {
val valor = vm.contador.observeAsState(0)
Column {
Text("Valor: ${valor.value}")
Button(onClick = { vm.incrementar() }) { Text("Sumar") }
}
}
View Binding¶
Cómo habilitar ViewBinding (XML)¶
android {
buildFeatures {
viewBinding = true
}
}
Ejemplo simple:¶
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.boton.setOnClickListener {
binding.texto.text = "¡Hola ViewBinding!"
}
}
}
RecyclerView¶
RecyclerView permite mostrar grandes cantidades de datos de forma eficiente.
Layout item (item_producto.xml)¶
<TextView
android:id="@+id/txtNombre"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
ViewHolder¶
class ProductoViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val txtNombre: TextView = view.findViewById(R.id.txtNombre)
}
Adapter¶
class ProductoAdapter(private val datos: List<Producto>) :
RecyclerView.Adapter<ProductoViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductoViewHolder {
val vista = LayoutInflater.from(parent.context)
.inflate(R.layout.item_producto, parent, false)
return ProductoViewHolder(vista)
}
override fun onBindViewHolder(holder: ProductoViewHolder, position: Int) {
val producto = datos[position]
holder.txtNombre.text = producto.nombre
}
override fun getItemCount(): Int = datos.size
}
Actividades¶
-
AC 507 (RA2 / CE2a / IC1 / 1p). Implementa un RecyclerView que muestre una lista de nombres:
"Ana", "Carlos", "Marta", "Elena", "Luis"Requisitos
- Adapter y ViewHolder propios.
- Layout
item_nombre.xmlcon un únicoTextView. - El RecyclerView debe mostrarse en la Activity con LinearLayoutManager.
NO es necesario MVVM todavía.
-
AC 508 (RA2 / CE2a / IC1 / 1p). ViewModel + RecyclerView + Listado de objetos. Combina los conceptos ya aprendidos:
- Crea un
data class Alumno(nombre: String, nota: Double). - Implementa un
AlumnoViewModelque exponga unLiveData<List<Alumno>>. - Prepara la lista inicial en el init del ViewModel.
- Muestra los datos en un RecyclerView usando un Adapter.
-
Muestra en cada elemento:
- nombre
- nota
- texto
"APTO"si nota ≥ 5 o"NO APTO"en caso contrario.
NO se requiere corrutinas ni repository.
Solo MVVM + RecyclerView + LiveData. - Crea un
-
PR 509 (RA2 / CE2a / IC1 / 5p). Construir la app TaskManager Ultra Mega Pro, que gestiona una lista de tareas simuladas. Debe cumplir:
- Arquitectura MVVM completa
- Carga de datos mediante corrutinas (
suspend,viewModelScope.launch) - RecyclerView funcional
- Posibilidad de marcar tareas como completadas
Evaluación (5 puntos)
Criterio Puntos MVVM bien estructurado 1.5 Corrutinas correctamente implementadas 1.5 RecyclerView 1 Gestión de estados 0.5 Limpieza y buenas prácticas 0.5 -
PR 510 (RA2 / CE2a / IC1 / 5p). Implementar una aplicación Android que funcione como una Pokédex básica, utilizando tecnologías modernas. La app debe mostrar:
- Una lista de Pokémon (imagen).
- Una pantalla de detalle.
- Modelo (mínimo). Se ha de utilizar un fichero que haga las veces de base de datos.
data class Pokemon( val id: Int, val nombre: String, val tipoPrincipal: String, val imagen: Int, val descripcion: String, var favorito: Boolean )-
ViewModel
-
Lista:
- LiveData/StateFlow con lista
- loading
- errors
-
-
Vista
- ProgressBar
-
RecyclerView. Item del RecyclerView:
- Imagen
-
Mensaje “No hay Pokémon”
- Uso obligatorio de ViewBinding
-
Pantalla de detalle. Debe mostrar:
- nombre
- imagen
- tipo
- descripción
Calificación
Criterio Puntos MVVM correcto 1 LiveData/StateFlow 1 Corrutinas 1 RecyclerView 1 Detalle 1