Índice de contenido
- ¿Qué es un listado?
- Column y Row: cuándo usarlos y cuándo no
- El problema con Column y Row
- Scroll en Column: primera señal de ineficiencia
- forEach y renderizado completo
- LazyColumn y LazyRow: Listados eficientes
- Comparación con RecyclerView (Android clásico)
- LazyVerticalGrid: listados en forma de grilla
- Adaptación a densidades de pantalla
- Reutilización de componentes en listados
- Cierre
- RecyclerViews y CardView el enfoque Legacy
- Los CardView
- Creando un RecyclerView
- Especificando el posicionamiento del RecyclerView con Layout Manager
- Especificando los datos y el modelo
- Definiendo el Adapter
- Invocando el Adapter anterior
- Conclusiones
- RecyclerView en Android con Kotlin: Mismo enfoque, eventos, clases pero diferente sintaxis
- La clase modelo de nuestro adaptador
- La Actividad principal: la que implementa el Adapter
- La clase adaptador: Define la lógica y comportamiento del RecyclerView
- El layout de nuestra actividad
- El layout de nuestro listado
- Conclusión
- Creando un filtro para los RecycleView en Android
Los listados son un componente muy interesante, porque en realidad son componentes (Composable) compuestos de otros componentes.
Cuando hablo de componentes, me refiero a estas clases/Composable que estamos utilizando: ya hemos visto los de texto y los de botones, que normalmente son bastante simples. Un botón, por ejemplo, no es más que texto con un comportamiento, aunque internamente reutiliza otros componentes para definir ese texto:
Text(
text = message
)Aquí ya empezamos a reutilizar lo que conocemos. Algo parecido hicimos antes con las cards cuando creamos una Tarjeta o Card como contenedor en Android Studio | Jetpack Compose; no solo creamos el componente de la tarjeta como tal, sino que también reutilizamos textos y botones, que son como pequeñas piezas de LEGO que nos permiten construir componentes más grandes.
En el caso de los listados ocurre exactamente lo mismo.
Un listado, por definición, ya tiene cierto comportamiento incorporado (de eso hablaremos en un momento), pero además nos permite reutilizar estas piezas pequeñas, incluyendo componentes más complejos como las cards, para construir una interfaz más rica.
¿Qué es un listado?
Un listado, en esencia, es un conjunto de elementos que se muestran en pantalla. Nada más.
En este caso puede ser un listado de textos, normalmente interactuable para ver detalles, aunque eso no viene al caso ahora.
Ten en cuenta que esto es solo una introducción básica, porque el tema tiene muchísima profundidad.
Aquí puedes ver el listado tanto en el emulador como en el preview.
Column y Row: cuándo usarlos y cuándo no
Ya conocemos los componentes Column y Row. Los hemos usado antes, por ejemplo, para botones o para las cards.
- Column apila elementos verticalmente, como una pila de libros.
- Row los coloca uno al lado del otro, en forma de fila.
Ambos funcionan igual, la diferencia es solo la orientación.
El problema con Column y Row
El problema aparece cuando intentamos usar Column o Row para listados grandes.
Para casos simples —como dos botones que siempre van a estar ahí— funcionan perfecto. Pero imagina ahora un listado de publicaciones:
5, 500 o incluso 5.000 elementos.
Es imposible mostrar todo eso de golpe en la pantalla de un dispositivo móvil. A diferencia de los botones, que son pocos y fijos, un listado grande no está pensado para renderizarse completo.
Por eso, para este tipo de casos, Column y Row no son la mejor opción.
Scroll en Column: primera señal de ineficiencia
Si quieres hacer scroll en un Column, tienes que agregar explícitamente el modificador:
@Composable
fun MessageInefficientList(messages: List<String>, modifier: Modifier = Modifier) {
/*Row*/Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())) {
messages.forEach { message ->
Text(
text = message
)
}
}
}Una vez lo haces, ya aparece la barra de scroll.
Pero aquí Android ya nos está dando una pista importante: este componente no está pensado para manejar scrolls grandes. Es para estructuras simples que caben en pantalla.
Esta es la primera evidencia de que no es la herramienta adecuada para listados extensos.
forEach y renderizado completo
Otro problema importante aparece cuando usamos forEach:
messages.forEach { message ->Este forEach se ejecuta completo al renderizar el componente, lo que significa que todos los elementos se construyen y renderizan de golpe.
Si tienes 5.000 elementos, los 5.000 se renderizan, aunque no se vean en pantalla.
Esto es claramente ineficiente y afecta rendimiento, memoria y batería.
LazyColumn y LazyRow: Listados eficientes
La forma correcta y eficiente de manejar listados es usando componentes lazy:
- LazyColumn
- LazyRow
El término lazy (perezoso) significa que solo se renderiza lo que se ve en pantalla.
Por ejemplo:
- Si en pantalla solo se ven los elementos del 1 al 23, solo esos se renderizan.
- El elemento 24, si no es visible, no se renderiza todavía.
- Se renderiza únicamente cuando hace falta.
Además, estos componentes ya son scrollables por defecto, no necesitas agregar nada extra.
Comparación con RecyclerView (Android clásico)
Si vienes del mundo Android tradicional, esto te va a resultar familiar (al final del post tienes la implementación con RecyclerView).
- Column y Row equivalen a un ListView antiguo: renderizan todo.
- LazyColumn y LazyRow equivalen a un RecyclerView.
El concepto de recycle es literal:
Cuando un elemento deja de verse, se recicla y se reutiliza para mostrar uno nuevo más abajo. No se crean miles de vistas nuevas, se reutilizan las existentes.
Esto es muchísimo más eficiente que renderizar 5.000 elementos de golpe.
@Composable
fun MessageList(messages: List<String>, modifier: Modifier = Modifier) {
/*LazyRow*/LazyColumn(modifier = modifier) {
items(messages) { message ->
Text(
text = message
)
}
}
}LazyVerticalGrid: listados en forma de grilla
Además de columnas y filas, tenemos:
- LazyVerticalGrid
- LazyHorizontalGrid
Estos nos permiten crear grillas adaptables.
Por ejemplo, usando GridCells.Adaptive, indicamos el tamaño mínimo que tendrá cada celda (por ejemplo, 200 dp), y el sistema decide cuántas columnas caben según el tamaño y densidad de la pantalla:
@Composable
fun MessageGrid(messages: List<String>, modifier: Modifier = Modifier) {
LazyVerticalGrid(
//modifier = Modifier.height(200.dp),
columns = GridCells.Adaptive(minSize = 200.dp)
) {
items(messages) { message ->
Text(
text = message
)
}
}
}Esto es extremadamente útil en Android, donde existen muchísimos tamaños y densidades de pantalla diferentes.
Adaptación a densidades de pantalla
- En el preview puedes notar que, debido a la baja densidad de píxeles, solo cabe un elemento por fila.
- En el emulador, que tiene mayor densidad, pueden aparecer dos o más columnas sin cambiar el código.
Esto demuestra cómo el diseño se adapta automáticamente sin necesidad de preguntar si el dispositivo está en portrait, landscape o qué tamaño tiene.
Reutilización de componentes en listados
Por último, algo muy importante: los listados están pensados para reutilizar componentes:
@Composable
fun MessageGrid(messages: List<String>, modifier: Modifier = Modifier) {
LazyVerticalGrid(
//modifier = Modifier.height(200.dp),
columns = GridCells.Adaptive(minSize = 200.dp)
) {
items(messages) { message ->
// Text(
// text = message
// )
CardBonito()
}
}
}En el ejemplo, reutilizamos CardBonito que implementamos en la entrada anterior y que es el componente más completo que hemos creado hasta ahora.
Simplemente lo llamamos dentro del listado y listo.

Este es el verdadero poder de los listados:
- No solo manejan grandes cantidades de datos de forma eficiente.
- También permiten reutilizar componentes previamente creados.
Cierre
Para cerrar, recuerda:
- Usa Column y Row solo para estructuras pequeñas y fijas.
- Usa LazyColumn, LazyRow o LazyGrid para listados grandes.
- Los componentes lazy son eficientes, reciclables y scrollables por defecto.
- Los listados existen para componer interfaces reutilizando otros componentes.
RecyclerViews y CardView el enfoque Legacy
Los RecyclerView al igual que los GridView y ListView, permite crear listados de ítems ya sea a través de listas o celdas; los RecyclerView pueden verse como una versión más flexible, potente y actualizada que estos y seguramente en algún momento serán el reemplazo definitivo de los mismos; la forma de funcionamiento es la misma empleada que sus predecesores como puedes ver en esta imagen:

Adapter que actúa como puente entre los datos y la vista.
Los CardView
El RecyclerView y los CardView forman parte de la librería de soporte; los CardView heredan de los ViewGroups más directamente de los FrameLayout y por ende es un elemento que nos permite definir muchas otros elementos dentro del mismo como por ejemplo los siguientes:
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="2dp"
card_view:cardCornerRadius="1dp">
<RelativeLayout
android:id="@+id/parent_body_rl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF5722"
android:orientation="vertical"
android:padding="2dp">
<LinearLayout
android:id="@+id/parent_body_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="2dp">
<LinearLayout
android:id="@+id/color_ll"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_margin="10dp"
android:background="#FF0000"
android:orientation="vertical" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:padding="2dp">
<TextView
android:id="@+id/name_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="texto 1"
android:textColor="#FFFFFF"
android:textSize="25sp" />
<TextView
android:id="@+id/description_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/info_text"
android:padding="10dp"
android:text="texto 2"
android:textColor="#FFFFFF"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</androidx.cardview.widget.CardView>Obteniendo el siguiente resultado:

En otras palabras, podemos emplear los CardView en conjunto con los RecyclerView; en donde el CardView define los ítems del listado.
Creando un RecyclerView
Antes que nada, necesitas agregar un elemento RecyclerView en el layout de nuestra Activity o Fragment:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recycler_view"
/>Al igual que cualquier otro elemento, para referenciar el RecyclerView anterior desde nuestra Activity empleamos el siguiente código Java:
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);Especificando el posicionamiento del RecyclerView con Layout Manager
A partir de aquí podemos apreciar algunos cambios con respecto al momento de crear los ListView y GridView; para definir el posicionamiento en que deseamos que renderice nuestra lista de ítems (en forma de listas o celdas):

Empleamos el siguiente código Java para que los elementos se posicionan en la lista a través de listados:
androidx.recyclerview.widget.LinearLayoutManager linearLayoutManager = new androidx.recyclerview.widget.LinearLayoutManager(context);
recyclerView.setLayoutManager(LinearLayoutManager);O el GridLayoutManager para que los elementos se posicionan en la lista a través de de celdas:
androidx.recyclerview.widget.GridLayoutManager gridLayoutManager = new androidx.recyclerview.widget.GridLayoutManager(context);
recyclerView.setLayoutManager(gridLayoutManager);Especificando los datos y el modelo
Para mayor facilidad al momento de manipular los datos crearemos un modelo Person para especificar una lista con nuestros datos que pasaremos posteriormente al Adapter.
public class Person {
String name;
String description;
String color;
Person(String name, String description,String color){
this.name = name;
this.description = description;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}Nada fuera de lo común, la persona costa de un nombre, una descripción (edad, etc) y el color favorito.
Definiendo el Adapter
El Adapter que utiliza los RecyclerView son muy parecidos a los empleados por los ListView y los GridView en lo que a su estructura y comportamiento se refiere; además de esto, vamos a emplear los ViewHolder para referencias más fácilmente los elementos que nos interese de nuestro listado; la definición del Adapter a continuación:
public class ListAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter<ListAdapter.ViewHolder>{
private ArrayList<Person> persons;
// Provee una referencia a cada item dentro de una vista y acceder a ellos facilmente
public static class ViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
// Cada uno de los elementos de mi vista
public TextView nameTextView,descriptionTextView;
public CardView cardView;
public LinearLayout colorLl;
public RelativeLayout parentBodyRl;
public ViewHolder(View v) {
super(v);
parentBodyRl = (RelativeLayout) v.findViewById(R.id.parent_body_rl);
cardView = (CardView) v.findViewById(R.id.card_view);
nameTextView = (TextView) v.findViewById(R.id.name_tv);
descriptionTextView = (TextView) v.findViewById(R.id.description_tv);
colorLl = (LinearLayout) v.findViewById(R.id.color_ll);
}
}
// Constructor
public ListAdapter(ArrayList<Person> persons) {
this.persons = persons;
}
// Create new views (invoked by the layout manager)
@Override
public ListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// inflo la vista (vista padre)
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_adapter, parent, false);
// creo el grupo de vistas
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Reemplaza en contenido de la vista
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.nameTextView.setText(persons.get(position).getName());
viewHolder.descriptionTextView.setText(persons.get(position).getDescription());
}
// Retorna el tamano de nuestra data
@Override
public int getItemCount() {
return persons.size();
}
}Como vemos en el código anterior, hay tres métodos que debemos sobreescribir:
El método getItemCount()que debe retornar el tamaño de la lista, o lo que es lo mismo, el tamaño de nuestra lista de elementos que se mostrarán en el listado.
El método onCreateViewHolder() permite inicializar todos los elementos que componen nuestra clase ViewHolder definida anteriormente; además definimos el layout (que puede variar para cada elemento) que utiliza cada elemento. También es posible definir los eventos en esta función.
En el método onBindViewHolder() se debe definir o enlazar los valores de los distintos campos.
Invocando el Adapter anterior
Desde nuestra Activity o Fragment, creamos un objeto del Adapter definido anteriormente:
ListAdapter listAdapter = new ListAdapter(persons);
audioRv.setAdapter(listAdapter);Y al correr nuestra aplicación en el emulador o dispositivo físico:

Conclusiones
En esta entrada dimos los primeros pasos con los RecyclerView y CardView disponibles en las últimas versiones de Android y en posteriores mediante la Librería de Soporte; pudimos notar que el funcionamiento y la implementación son muy parecidas a lo que estamos acostumbrados con los GridView y ListView; en siguientes entradas veremos lo sencillo que resulta de implementar otras funcionalidades a las RecyclerView y CardView como son el swipe y algunas sencillas animaciones para ocultar secciones de los CardView.
En una anterior entrada hablamos un poco sobre el futuro (y presente) de Kotlin en el mundo del desarrollo de aplicaciones Android empleando este lenguaje de programación que poco a poco intenta ir reemplazando a Java como lenguaje primario y oficial en el desarrollo de aplicaciones Android; una de las razones que están expuestas en esa entrada que puedes consultar, el la simplificación de las distintos elementos que podemos desarrollar, un ejemplo de esto es el desarrollo de los RecyclerView de un adapter como veremos a continuación.
Para darle continuidad y soporte a algunos de los argumentos realizados en la anterior entrada, hoy veremos cómo realizar un sencillo listado de elementos mediante un RecyclerView empleando netamente Kotlin como lenguaje de programación; es decir, nada de Java.
RecyclerView en Android con Kotlin: Mismo enfoque, eventos, clases pero diferente sintaxis
Las clases a emplear para crear un listado con RecyclerView con Kotlin serán las mismas empleadas para el desarrollo de un Recyclerview empleando Java. la clase modelo, adaptador y por supuesto la actividad, también necesitaremos un par de layouts; uno para la actividad y otro para el listado que vendría siendo nuestro RecyclerView.
La clase modelo de nuestro adaptador
Aquí debemos de definir la clase o modelo que definirá nuestro objeto, si es una persona, si es un automóvil si es una computadora o en este caso definimos una clase Item que contará con un identificador y una descripción que sería de las cosas más sencillas que podemos definir:
data class Item(val id: Long, val title: String, val description: String)La Actividad principal: la que implementa el Adapter
Una novedad que tiene Kotlin con respecto a Java es que mediante el nombre del identificador del elemento en el layout; (por ejemplo android:id="@+id/rv_item" para definir el identificador del RecyclerView en el layout de nuestra actividad) es suficiente para referenciar en la actividad o clase que establece el contenido de dicha vista, y por lo tanto en Kotlin ya no es necesario establecer el valor de dichas variables como se hace en Java-Android; esto se logra mediante un plugin/include que se importa al momento de hacer la referencia y de manera automática por el Android Studio (import kotlinx.android.synthetic.main.activity_main.*):
RecyclerView rv_item = (RecyclerView) findViewById(R.id.rv_item);Aunque también podemos hacer la versión clásica que empleamos en Android con Java; finalmente el código de la Actividad:
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import mykotlin.dl.recyclerview.recyclerviewkotlin.adapter.ItemAdapter
import mykotlin.dl.recyclerview.recyclerviewkotlin.model.Item
import kotlin.dl.recyclerview.recyclerviewkotlin.R
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = getLists()
rv_item.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this)
rv_item.hasFixedSize()
rv_item.adapter = ItemAdapter(items)
rv_item.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this)
rv_item.adapter = ItemAdapter(items)
}
fun getLists(): ArrayList<Item> {
var lists = ArrayList<Item>()
lists.add(Item(1, "Item 1", "Descripcion 1"))
lists.add(Item(1, "Item 2", "Descripcion 2"))
lists.add(Item(1, "Item 3", "Descripcion 3"))
lists.add(Item(1, "Item 4", "Descripcion 4"))
return lists;
}
}
La actividad anterior se encarga de definir el tipo de layout (que en este caso será listado tipo ListView) y de popular (llenar) un ArrayList de tipo Item con algunos datos de prueba y de por supuesto, crear una instancia del adaptador que creará el listado que definimos en el siguiente bloque dentro de esta entrada.
La clase adaptador: Define la lógica y comportamiento del RecyclerView
La clase adaptador, que es la que se encarga de definir el comportamiento y estilo de cada uno de los componentes del listado, como podemos ver, se realiza el mismo nombrado para sobrescribir cada uno de las funciones heredadas de la clase RecyclerView.Adapter; nuevamente empleamos el nombre de los items de cada elemento definido en la vista sin necesidad de referenciarlos primero (ej: itemView.tv_description con el import autogenerado del IDE import kotlinx.android.synthetic.main.adapter_item.view.*):
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.adapter_item.view.*
import kotlin.dl.recyclerview.recyclerviewkotlin.R
import mykotlin.dl.recyclerview.recyclerviewkotlin.model.Item
class ItemAdapter (val userList: ArrayList<Item>) : androidx.recyclerview.widget.RecyclerView.Adapter<ItemAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.adapter_item, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(userList[position])
}
override fun getItemCount(): Int {
return userList.size
}
class ViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
fun bindItems(item: Item) {
itemView.tv_title.text=item.title
itemView.tv_description.text=item.description
}
.
}
}
Sobreescribimos los métodos de siempre, onBindViewHolder para retornar el ítem actual getItemCount para retornar el tamaño de la colección de objetos onCreateViewHolder para especificar el layout de nuestro listado así como componer la vista y por último la clase ViewHolder que permite definir cada uno de los elementos compuestos en nuestro layout y especificar su contenido y por supuesto el método constructor en donde inicializamos los componentes fundamentales o que requiramos.
El layout de nuestra actividad
En cuanto a los layouts, se mantienen exactamente los mismos que usamos en Java-Android; más que eso no hay mucho que decir, desde nuestra actividad especificamos un elemento RecyclerView y el otro layout es el especificado por empleado por nuestro listado:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mykotlin.dl.recyclerview.recyclerviewkotlin.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="@+id/rv_item"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
El layout de nuestro listado
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>Como podemos ver la lógica empleada es la misma que empleamos con Java, te invito a que revises algunas de las anteriores entradas que están listadas al final de esta entrada y las compares con estas para ver la similitud que existe entre ambas.
Finalmente, obtenemos un listado como este:

Puedes darle el estilo que quieras modificando el layout o el XML de estilo como prefieras, para darle el diseño a tu gusto; la idea en esta entrada era mostrar cómo crear un RecyclerView empleando netamente Kotlin como lenguaje de programación y no centrarnos en el diseño.
Conclusión
Cómo podemos ir viendo, Kotlin es un lenguaje completo que nos permite desarrollar aplicaciones en Android más fácil (al menos en lo que a sintaxis se refiere) que con Java.
Creando un filtro para los RecycleView en Android

En esta entrada veremos cómo crear un filtro para nuestros RecycleView; en una entrada anterior vimos cómo crear listados a través de listas y grids con el RecycleView hoy veremos el mismo proceso pero con un extra el cual consiste en crear un filtro para el listado anterior a través (lógicamente) por un campo de búsqueda con un simple EditText.
En nuestro adaptador crearemos una clase extra que llamaremos CustomFilter que lucirá de la siguiente manera:
public class CustomFilter extends Filter {
private ListAdapter listAdapter;
private CustomFilter(ListAdapter listAdapter) {
super();
this.listAdapter = listAdapter;
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
personsFilter.clear();
final FilterResults results = new FilterResults();
if (constraint.length() == 0) {
personsFilter.addAll(persons);
} else {
final String filterPattern = constraint.toString().toLowerCase().trim();
for (final Person person : persons) {
if (person.getName().toLowerCase().contains(filterPattern)) {
personsFilter.add(person);
}
}
}
results.values = personsFilter;
results.count = personsFilter.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
this.listAdapter.notifyDataSetChanged();
}
}Además de agregar un implements para implementar la clase Filterable en nuestro Adapter:
public class ListAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter<ListAdapter.ViewHolder> implements Filterable {
private ArrayList<Person> persons;
private ArrayList<Person> personsFilter;
private CustomFilter mFilter;
...
}
public ListAdapter(ArrayList<Person> persons) {
this.persons = persons;
this.personsFilter = new ArrayList<>();
this.personsFilter.addAll(persons);
this.mFilter = new CustomFilter(ListAdapter.this);
}En nuestro adaptador y el método asociado getFilter que sobreescribimos:
@Override
public Filter getFilter() {
return mFilter;
}Un pequeño dato que tenemos que hacer en nuestro adaptador es crear otra lista la cual contiene la data filtrada por el usuario y otro listado que contiene el total de la data que conforma nuestro listado sin aplicar ningún filtro; teniendo esto en cuenta nuestra el constructor y así como otros métodos de control quedaran definidos de la siguiente manera:
// Constructor
public ListAdapter(ArrayList<Person> persons) {
this.persons = persons;
this.personsFilter = new ArrayList<>();
this.personsFilter.addAll(persons);
this.mFilter = new CustomFilter(ListAdapter.this);
}
...
@Override
public int getItemCount() {
return personsFilter.size();
}
...
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
viewHolder.nameTextView.setText(personsFilter.get(position).getName());
viewHolder.descriptionTextView.setText(personsFilter.get(position).getDescription());
viewHolder.colorLl.setBackgroundColor(Color.parseColor(personsFilter.get(position).getColor()));
}Cómo ves empleamos el ArrayList filtrable en vez del ArrayList completo.
Tenemos listo el adaptador, ahora falta configurar en nuestra actividad o fragment el campo de búsqueda para poder filtrar nuestro listado y los eventos asociados; para ello emplearemos un EditText como comentamos anteriormente:
etSearchBox = (EditText) findViewById(R.id.etSearchBox);Y el evento escuchador (listener) que se activa al ingresar/remover texto sobre el mismo:
etSearchBox.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
listAdapter.getFilter().filter(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});Cómo puedes ver, al cambiar el texto se invoca el método filter pasando como parámetro el texto a filtrar.
Puedes especificar el funcionamiento del filtro en la clase CustomFilter del adaptador definiendo el comportamiento en el momento en el que se realiza el llenado de personsFilter al realizar la comparación:
if (person.getName().toLowerCase().contains(filterPattern))En nuestro caso nos interesa que NO sea sensible a mayúsculas/minúsculas y que contenga (contains) la palabra o sección clave y de esta forma tener un filtro bastante flexible, pero puedes expresarlo como desees.
El siguiente paso El Snackbar en Android Studio