La arquitectura MVVM (Model-View-ViewModel) es un patrón de diseño que facilita la separación de la lógica de negocio y la interfaz de usuario en aplicaciones Android. Este patrón ayuda a crear aplicaciones más mantenibles, escalables y testables.

Objetivos del Módulo

  • Comprender los conceptos básicos de la arquitectura MVVM.
  • Implementar MVVM en una aplicación Android.
  • Utilizar ViewModel y LiveData para gestionar y observar datos.
  • Integrar un repositorio para la gestión de datos.

Conceptos Clave

  1. ¿Qué es MVVM?

MVVM es un patrón de diseño que divide la aplicación en tres componentes principales:

  • Model: Representa la capa de datos de la aplicación. Gestiona la lógica de negocio y la comunicación con la base de datos o servicios web.
  • View: Representa la interfaz de usuario. Observa los datos expuestos por el ViewModel.
  • ViewModel: Actúa como un intermediario entre el Model y la View. Gestiona la lógica de presentación y expone los datos que la View necesita.

  1. Beneficios de MVVM

  • Separación de responsabilidades: Cada componente tiene una responsabilidad clara, lo que facilita el mantenimiento y la escalabilidad.
  • Testabilidad: La lógica de negocio y de presentación se puede probar de manera independiente.
  • Reutilización de código: Los ViewModels pueden ser reutilizados en diferentes vistas.

Implementación de MVVM en Android

Paso 1: Configuración del Proyecto

Asegúrate de tener las siguientes dependencias en tu archivo build.gradle:

dependencies {
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}

Paso 2: Crear el Modelo (Model)

El modelo representa la fuente de datos. Puede ser una base de datos, una API o cualquier otra fuente de datos.

data class User(val id: Int, val name: String, val email: String)

Paso 3: Crear el Repositorio

El repositorio actúa como una fuente única de verdad para los datos. Gestiona la lógica de obtención y almacenamiento de datos.

class UserRepository {
    private val users = listOf(
        User(1, "John Doe", "[email protected]"),
        User(2, "Jane Smith", "[email protected]")
    )

    fun getUsers(): List<User> {
        return users
    }
}

Paso 4: Crear el ViewModel

El ViewModel expone los datos que la vista necesita y gestiona la lógica de presentación.

class UserViewModel : ViewModel() {
    private val userRepository = UserRepository()
    private val _users = MutableLiveData<List<User>>()
    val users: LiveData<List<User>> get() = _users

    init {
        loadUsers()
    }

    private fun loadUsers() {
        _users.value = userRepository.getUsers()
    }
}

Paso 5: Crear la Vista (View)

La vista observa los datos expuestos por el ViewModel y actualiza la interfaz de usuario en consecuencia.

<!-- res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/userTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Users" />

</LinearLayout>
// MainActivity.kt
class MainActivity : AppCompatActivity() {
    private lateinit var userViewModel: UserViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
        val userTextView: TextView = findViewById(R.id.userTextView)

        userViewModel.users.observe(this, Observer { users ->
            userTextView.text = users.joinToString { it.name }
        })
    }
}

Ejercicio Práctico

Ejercicio

  1. Crea una aplicación Android que utilice la arquitectura MVVM para mostrar una lista de productos.
  2. Implementa un ProductRepository que gestione una lista de productos.
  3. Crea un ProductViewModel que exponga los productos a la vista.
  4. Diseña una vista que observe los datos del ProductViewModel y los muestre en un RecyclerView.

Solución

Paso 1: Crear el Modelo

data class Product(val id: Int, val name: String, val price: Double)

Paso 2: Crear el Repositorio

class ProductRepository {
    private val products = listOf(
        Product(1, "Laptop", 999.99),
        Product(2, "Smartphone", 499.99)
    )

    fun getProducts(): List<Product> {
        return products
    }
}

Paso 3: Crear el ViewModel

class ProductViewModel : ViewModel() {
    private val productRepository = ProductRepository()
    private val _products = MutableLiveData<List<Product>>()
    val products: LiveData<List<Product>> get() = _products

    init {
        loadProducts()
    }

    private fun loadProducts() {
        _products.value = productRepository.getProducts()
    }
}

Paso 4: Crear la Vista

<!-- res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/productRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
// MainActivity.kt
class MainActivity : AppCompatActivity() {
    private lateinit var productViewModel: ProductViewModel
    private lateinit var productAdapter: ProductAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        productViewModel = ViewModelProvider(this).get(ProductViewModel::class.java)
        val productRecyclerView: RecyclerView = findViewById(R.id.productRecyclerView)
        productAdapter = ProductAdapter()

        productRecyclerView.layoutManager = LinearLayoutManager(this)
        productRecyclerView.adapter = productAdapter

        productViewModel.products.observe(this, Observer { products ->
            productAdapter.submitList(products)
        })
    }
}
// ProductAdapter.kt
class ProductAdapter : ListAdapter<Product, ProductAdapter.ProductViewHolder>(ProductDiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_product, parent, false)
        return ProductViewHolder(view)
    }

    override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val productNameTextView: TextView = itemView.findViewById(R.id.productNameTextView)
        private val productPriceTextView: TextView = itemView.findViewById(R.id.productPriceTextView)

        fun bind(product: Product) {
            productNameTextView.text = product.name
            productPriceTextView.text = product.price.toString()
        }
    }

    class ProductDiffCallback : DiffUtil.ItemCallback<Product>() {
        override fun areItemsTheSame(oldItem: Product, newItem: Product): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Product, newItem: Product): Boolean {
            return oldItem == newItem
        }
    }
}

Conclusión

En esta sección, hemos aprendido a implementar la arquitectura MVVM en una aplicación Android. Hemos visto cómo separar la lógica de negocio y la interfaz de usuario utilizando ViewModel y LiveData. Esta arquitectura no solo mejora la mantenibilidad y escalabilidad de la aplicación, sino que también facilita la realización de pruebas unitarias y de integración. En el siguiente módulo, exploraremos la inyección de dependencias con Dagger para mejorar aún más la modularidad y testabilidad de nuestras aplicaciones.

© Copyright 2024. Todos los derechos reservados