Saltar a contenido

Creación de listas en Android

ListView

ListView muestra una colección de vistas desplazables verticalmente, en la que cada vista se sitúa inmediatamente debajo de la anterior.

Para mostrar una lista, se puede incluir una vista de lista en el archivo XML de diseño.
Una ListView es una vista de adaptador que no conoce los detalles, como el tipo y el contenido, de las vistas que contiene.
En su lugar, solicita vistas bajo demanda a un ListAdapter según sea necesario, por ejemplo, para mostrar nuevas vistas a medida que el usuario se desplaza.

Para mostrar elementos en la lista, se llama a setAdapter(android.widget.ListAdapter) para asociar un adaptador con la lista.

Si se desea una vista más personalizada para cada elemento del conjunto de datos, se puede implementar un ListAdapter, extendiendo BaseAdapter y configurando las vistas en el método getView(...).

ListView intenta reutilizar las vistas para mejorar el rendimiento y evitar retardos en el desplazamiento.
Para aprovechar esta característica, se debe comprobar si el convertView proporcionado a getView(...) es nulo antes de crear o inflar un nuevo objeto de vista.

Para especificar una acción cuando el usuario hace clic en un elemento, puede utilizarse setOnItemClickListener().

Aunque existen ejemplos antiguos que usan ListActivity o ListFragment, hoy se recomienda utilizar una subclase de Activity o Fragment y añadir la lista directamente al diseño para tener un control más directo sobre la vista y el adaptador.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/titulo"
        android:text="Listado de nombres"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="8dp" />

    <ListView
        android:id="@+id/listViewNombres"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
package com.example.listviewdemo

import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.activity.ComponentActivity

class MainActivity : ComponentActivity() {

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

        val listView = findViewById<ListView>(R.id.listViewNombres)

        // Lista de ejemplo
        val nombres = listOf("Ana", "Luis", "María", "Javier", "Sofía")

        // Adaptador sencillo
        val adaptador = ArrayAdapter(this, android.R.layout.simple_list_item_1, nombres)

        // Asignación del adaptador al ListView
        listView.adapter = adaptador
    }
}

RecyclerView

RecyclerView es la evolución directa de ListView y GridView, y forma parte de la librería AndroidX.
Ofrece una arquitectura más eficiente y flexible para mostrar listas o rejillas de elementos, permitiendo un control total sobre cómo se crean, organizan y animan las vistas.

Componentes principales

  1. Adapter (RecyclerView.Adapter)
    Es el responsable de crear las vistas de cada elemento y vincular los datos con ellas.
    Se deben implementar los métodos onCreateViewHolder(), onBindViewHolder() y getItemCount().

  2. ViewHolder (RecyclerView.ViewHolder)
    Representa cada ítem visible en la lista y mantiene referencias a las vistas para evitar llamadas repetidas a findViewById().
    Mejora el rendimiento y simplifica la vinculación de datos.

  3. LayoutManager
    Define la disposición visual de los elementos. Android proporciona tres implementaciones principales:
    - LinearLayoutManager → listas verticales u horizontales.
    - GridLayoutManager → cuadrículas.
    - StaggeredGridLayoutManager → listas tipo mosaico.

También es posible crear un LayoutManager personalizado.

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerViewProductos"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:padding="12dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tvNombre"
        android:textSize="18sp"
        android:textStyle="bold"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tvPrecio"
        android:textSize="16sp"
        android:textColor="@android:color/darker_gray"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
package com.example.recyclerviewdemo

data class Producto(val nombre: String, val precio: Double)
package com.example.recyclerviewdemo

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ProductoAdapter(private val productos: List<Producto>) :
    RecyclerView.Adapter<ProductoAdapter.ProductoViewHolder>() {

    inner class ProductoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvNombre: TextView = itemView.findViewById(R.id.tvNombre)
        val tvPrecio: TextView = itemView.findViewById(R.id.tvPrecio)
    }

    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 = productos[position]
        holder.tvNombre.text = producto.nombre
        holder.tvPrecio.text = "Precio: ${producto.precio} €"
    }

    override fun getItemCount() = productos.size
}
package com.example.recyclerviewdemo

import android.os.Bundle
import androidx.appcompat.activity.ComponentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : ComponentActivity() {

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

        val recyclerView = findViewById<RecyclerView>(R.id.recyclerViewProductos)
        recyclerView.layoutManager = LinearLayoutManager(this)

        val listaProductos = listOf(
            Producto("Teclado mecánico", 49.99),
            Producto("Ratón inalámbrico", 19.95),
            Producto("Monitor 24\"", 149.0),
            Producto("Auriculares", 29.99)
        )

        recyclerView.adapter = ProductoAdapter(listaProductos)
    }
}

LazyColumn (Jetpack Compose)

En el entorno moderno de Android, cuando trabajamos con Jetpack Compose, el componente recomendado para mostrar listas de elementos desplazables es LazyColumn.
Este componente cumple el mismo propósito que RecyclerView, pero dentro del paradigma declarativo de Compose: solo renderiza los elementos visibles en pantalla, optimizando así el rendimiento.

Un LazyColumn se utiliza para mostrar colecciones de datos de manera vertical y eficiente:

@Composable
fun ListaDeNombres() {
    val nombres = listOf("Ana", "Luis", "María", "Javier")
    LazyColumn {
        items(nombres) { nombre ->
            Text(
                text = nombre,
                modifier = Modifier
                    .padding(16.dp)
                    .fillMaxWidth()
            )
        }
    }
}

El prefijo “Lazy” indica que los elementos se crean y renderizan de forma perezosa, es decir, únicamente cuando son necesarios para ser mostrados.
Esto mejora el rendimiento y reduce el consumo de memoria, de forma similar al funcionamiento interno de RecyclerView.

A diferencia del enfoque imperativo con ListView o RecyclerView, LazyColumn gestiona automáticamente la creación, eliminación y recomposición de las vistas según el estado del scroll o los datos observables.
Además, puede combinarse con funciones de estado (remember, ViewModel, Flow, etc.) y con componentes como LazyRow o LazyVerticalGrid para crear disposiciones más complejas.

Comparativa

Característica ListView RecyclerView LazyColumn (Compose)
Paradigma Imperativo Imperativo (modular) Declarativo
Reutilización de vistas Manual (convertView) Automática con ViewHolder Interna mediante recomposición
Disposición Solo vertical Vertical, horizontal o grid Vertical (con LazyRow para horizontal)
Animaciones Limitadas Integradas Declarativas (animate* APIs)
Adaptador ArrayAdapter / BaseAdapter RecyclerView.Adapter items() en el bloque LazyColumn
Eficiencia Correcta Alta Muy alta
Uso recomendado (2025) Solo para mantenimiento de apps antiguas Estándar para apps con XML Estándar para apps en Compose

Actividades

  • AC 304 (RA2 / CE2a / IC1 / 1p). Diseña una aplicación sencilla que muestre una lista de los corredores actuales de la F1 utilizando un ListView. Los datos deben cargarse desde una lista definida en el código.

  • AC 305 (RA2 / CE2a / IC1 / 1p). Implementa una aplicación básica con RecyclerView que muestre una lista de productos de un kebab (nombre y precio).

  • AC 306 (RA2 / CE2a / IC1 / 1p). Crea una aplicación con Jetpack Compose que utilice un LazyColumn para mostrar una lista de tareas pendientes. Cada elemento debe incluir el nombre de la tarea y un pequeño texto descriptivo. Añade un encabezado con el título “Mis tareas”. Las tareas se le solicitan al usuario.