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 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.
Dato adicional
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.
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter