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¶
-
Adapter (
RecyclerView.Adapter)
Es el responsable de crear las vistas de cada elemento y vincular los datos con ellas.
Se deben implementar los métodosonCreateViewHolder(),onBindViewHolder()ygetItemCount(). -
ViewHolder (
RecyclerView.ViewHolder)
Representa cada ítem visible en la lista y mantiene referencias a las vistas para evitar llamadas repetidas afindViewById().
Mejora el rendimiento y simplifica la vinculación de datos. -
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
RecyclerViewque 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
LazyColumnpara 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.