«

»

jun 21 2012

Filtrar listas en Android

Hoy os traemos un pequeño tip para implementar en nuestras apps que seguro os será de utilidad a más de uno. En muchas aplicaciones actuales ya podemos encontrar el típico input text en la parte superior que nos sirve para realizar búsquedas sobre la lista actual mostrada y que estas búsquedas se apliquen en tiempo real filtrando los resultados del listado. Pues a bien a continuación os mostramos el código de ejemplo para tal menester. El algoritmo no tiene mayor complejidad, tan solo configuraremos un listener que responderá a los cambios de texto en el input text recargando el adapter de la lista con los nuevos datos:


main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/LinearLayout01"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/EditText01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Search" >
    </EditText>

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

</LinearLayout>

 

 

FilterListActivity.java

import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class FilterListActivity extends Activity {
	private ListView lv;
	private EditText et;
	private String listview_array[] = { "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN" };
	private ArrayList<String> array_sort = new ArrayList<String>();
	int textlength = 0;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		lv = (ListView) findViewById(R.id.ListView01);
		et = (EditText) findViewById(R.id.EditText01);
		
		lv.setAdapter(new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, listview_array));

		et.addTextChangedListener(new TextWatcher() {
			public void afterTextChanged(Editable s) {
				// Abstract Method of TextWatcher Interface.
			}

			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {
				// Abstract Method of TextWatcher Interface.
			}

			public void onTextChanged(CharSequence s, int start, int before, int count) {
				textlength = et.getText().length();
				array_sort.clear();
				
				for (int i = 0; i < listview_array.length; i++) {
					if (textlength <= listview_array[i].length()) {
						if (et.getText().toString().equalsIgnoreCase((String) listview_array[i].subSequence(0, textlength))) {
							array_sort.add(listview_array[i]);
						}
					}
				}
				
				lv.setAdapter(new ArrayAdapter<String>(FilterListActivity.this, android.R.layout.simple_list_item_1, array_sort));
			}
		});
	}
}

 

 

Como podéis comprobar es muy sencillo y pronto lo tendréis funcionando. Espero que os sirva de ayuda y hasta la próxima.

 

vía | android-helper.blogspot.mx

 

Espero que os haya gustado y si es así no lo dudéis y ¡¡compartid lo aprendido!!

 

Acerca del autor

JMPergar

Mobile Developer at @BeRepublic & Founder of @AndroCode. Silver Speaker & Member of Core Team at @GDGBarcelona.

  • José Yébenes

    Hola, como sería cuando está la lista filtrada para asignar a cada fila una acción.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      ¿Te refieres a asociar una acción al click?

      • Davi

        A mi me funciona con él onitemclicklistener

  • Luis Ernesto Castillo Alfaro

    Hola JMPergar que tal una consulta estoy desarrollando en android y quisiera saber como puedo llenar un listview con un query de sqlite en android.
    Gracias Hasta luego.

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Te recomiendo eches un ojo a las dos librerias para base de datos que tenemos explicadas en el blog:
      - Android Data Framework
      - ADA Framework

      Ambas te facilitan el trabajo con BD y en ambas exponemos un ejemplo con listas.

  • Davi

    Hola y gracias por el tuto.
    El arrayadapter lo lleno con una bd sqlite, cuando hago una consulta de una palabra dentro de una frase me llena el listview correctamente, el problema me viene cuando quiero hacer la consulta de dos palabras no consecutivas de la misma frase. Ya se que en el tutorial hacéis el array con palabras solas pero me envalentoné y ahora no me salgo. Gracias

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      Si me das un poco mas de información de como lo has hecho a lo mejor puedo ayudarte ;)

  • JARAPETO

    Saludos cordiales, es muy Buena explicación
    A ver si me pueden ayudar, estoy iniciando en androide y estoy trabajando con un ORM para recuperar datos de una base, ahora quiero crear una lista de objetos, que contenga título, subtitulo, descripción que es lo que contiene mi objeto, esto lo he logrado, pero hasta el momento no se cómo lograr que se me active el filtro por el título del objeto, el filtrado con objetos personalizados no me sirve, esto me sería útil ya que tendré una lista de alrededor de más de 3000 artículos, si tienen alguna idea se los agradecería mucho

    • http://www.linkedin.com/in/jmpergar JMPergar | Editor Jefe

      No termino de entender donde tienes el problema, la única diferencia es que dentro del array en vez de tener Strings tendrías objetos con tu información y a partir de ahí compararías los datos que necesitaras para filtrar la lista.

  • Pepe

    Hola, muchas gracias por el tutorial. Tengo el mismo problema que JArapeto, lo he intentado solucionar accediendo al objeto almacenado en el array, y a través del método getApellido() de mi objeto (en otra clase) accedo a la información que deseo. De todas formas no me acaba de funcionar el control. Pongo el código y si alguien puee echarme una mano, gracias por adelantado.

    public void onTextChanged(CharSequence s, int start, int before, int count) {
    textlength = et.getText().length();
    array_sort.clear();

    for (int i = 0; i < datos.size(); i++) {
    if (textlength <= datos.get(i).getApellido().length()) {
    if (et.getText().toString().equalsIgnoreCase((String) datos.get(i).getApellido().subSequence(0, textlength))) {
    array_sort.add(datos.get(i));
    }
    }
    }

    l1.setAdapter(new AdaptadorAlumno(MainActivity.this, array_sort));
    }

    • http://www.linkedin.com/in/jmpergar JMPergar

      ¿Pero te da error? ¿O simplemente no funciona? Se me ocurre que sea necesario que incluyas el casting del datos.get(i) para que reconozca bien de que clase es.

      Para mas limpieza lo sacaria antes del if:

      MiClase mc = (MiClase) datos.get(i);

      Y trabajaria con mc a partir de ese momento. No se si será eso pero con la información que me das solo puedo especular un poco.

      • Pepe

        No me da ningún error de compilación, pero en ejecución no hace el filtrado correctamente, de hecho el filtrado no tiene ningún sentido. Por concretar un poco cuando hago el datos.get(i), ya estoy obteniendo un objeto de mi clase, creo que no es necesario hacer el casting. En teoría cuando añado el getaApellido me devuelve el string correspondiente (un dato de esa clase), si no pongo el casting (String) me da un error de compilación. ¿Ando equivocado en algo o el razonamiento es correcto?
        Te añado la definición de la clase donde tengo los datos:
        public class datosAlumno {

        private String nombre=”";
        private String apellidos=”";

        public datosAlumno(String ap, String nom){
        nombre = nom;
        apellidos = ap;
        }

        public String getNombre(){
        return nombre;
        }

        public String getApellido(){
        return apellidos;
        }
        }

        • http://www.linkedin.com/in/jmpergar JMPergar

          Pues en principio lo veo todo correcto. ¿Has mirado al LogCat a ver si te da algun mensaje?

  • Pepe

    Te pongo una traza, tengo estos datos:
    baljo
    pepe

    baljor
    juan

    cases
    luis

    caseta
    ana

    En el buscar pongo una b y obtengo
    baljo
    pepe

    baljor
    juan

    Borro y actualiza la lista de nuevo con toda la información. Vuelvo a buscar por c y sale lo mismo
    baljo
    pepe

    baljor
    juan

    ¿Tiene algún sentido?

    • http://www.linkedin.com/in/jmpergar JMPergar

      Pues me dejas loco, la verdad es que no tiene ningun sentido, si cuando borras te siguiera saliendo lo mismo pensaria que el valor se ha quedado cacheado, pero si al borrar va bien y al empezar a escribir de nuevo mantiene el valor anterior me resulta muy extraño…. ¿si pones otros valores se sigue comportando igual? Es decir, ¿si ponemos primero la c y luego la b pasaria lo mismo pero a la inversa?

    • Pepe

      Me he dado cuenta que me filtra bien la cantidad a elementos a mostrar, pero siempre visualiza los n primeros del listView, sin hacer caso al array_sort, creo que el problema lo tengo en mi activity principal, al crear el adaptador por primera vez, pero no sé como solucionarlo.

      • Pepe

        El código de esta es:

        l1=(ListView)findViewById(R.id.listView1);

        cInicial=baseDatos.rawQuery(“select nombre, apellido from contacto order by apellido, nombre”,null);
        if (cInicial.moveToFirst()){
        datos=getDatos(cInicial);
        }
        else Toast.makeText(getApplicationContext(), “No hay datos en la Base de Datos”, Toast.LENGTH_LONG).show();
        adaptador=new AdaptadorAlumno(this,datos);
        l1.setAdapter(adaptador);

        //contro de la búsqueda
        et = (EditText) findViewById(R.id.editBuscar);
        et.addTextChangedListener(new TextWatcher() {
        public void afterTextChanged(Editable s) {
        // Abstract Method of TextWatcher Interface.
        }

        public void beforeTextChanged(CharSequence s, int start, int count,
        int after) {
        // Abstract Method of TextWatcher Interface.
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
        textlength = et.getText().length();
        array_sort.clear();

        for (int i = 0; i < datos.size(); i++) {
        if (textlength <= datos.get(i).getApellido().length()) {
        if (et.getText().toString().equalsIgnoreCase((String) datos.get(i).getApellido().subSequence(0, textlength))) {
        array_sort.add(datos.get(i));
        }
        }
        }

        l1.setAdapter(new AdaptadorAlumno(MainActivity.this, array_sort));
        }
        });

        Los … son porque tengo otros botones en la activity.

        • http://www.linkedin.com/in/jmpergar JMPergar

          Llevo un rato leyendo una y otra vez tu código pero sin depurar es difícil y me esta matando ya la curiosidad por que no veo ningún error. Si descubres que es no dudes en compartirlo.

          • Pepe

            He descubierto el error, el problema lo tengo en la definición del adaptador, estaba utilizando el arrayList directamente sin pasarlo por parámetro dentro del método getView.
            Sinto las molestias. Os pongo el código del adaptador tal y como lo tengo ya corregido:

            class AdaptadorContacto extends ArrayAdapter {
            Activity context;
            ArrayList datos;
            AdaptadorContacto(Activity context,ArrayList datos) {
            super(context, R.layout.listitem_contacto, datos);
            this.datos=datos;
            this.context = context;
            }

            public View getView(int position, View convertView, ViewGroup parent) {
            View item =convertView;
            ViewHolder holder;

            if (item==null){
            LayoutInflater inflater = context.getLayoutInflater();
            item = inflater.inflate(R.layout.listitem_contacto, null);

            holder=new ViewHolder();
            holder.titulo=(TextView)item.findViewById(R.id.lblApellido);
            holder.subtitulo=(TextView)item.findViewById(R.id.lblNombre);
            item.setTag(holder);
            }
            else holder=(ViewHolder) item.getTag();
            holder.titulo.setText(this.datos.get(position).getApellido());
            holder.subtitulo.setText(this.datos.get(position).getNombre());

            return(item);
            }
            }

            static class ViewHolder{
            TextView titulo;
            TextView subtitulo;
            }

            Gracias por todo

  • beto

    buen tutorial quiesiera saber si hay algun evento que se desencadene al darle scroll al list view??
    lo que quiero hacer es por ejemplo mosttrar 10 resultados qe coincidadn con el texto pero cuando doy scroll y muestro el ultimo resultado que me cargue otros 10 (mostrando ahora 20 resultados)

    no se si se pueda hacer ya que e buscado pero no e podido encontrar como hacerlo???

    • http://www.linkedin.com/in/jmpergar JMPergar

      No sabría decirte con exactitud pero diría que existen librerías para este menester.

  • edward perines

    yo utilice este metodo de esta forma y filtra sin problemas

    public void onTextChanged(CharSequence s, int start, int before, int count) {
    // TODO Auto-generated method stub
    EditText te = (EditText) findViewById(R.id.txtclientes);
    array_busqueda.clear();
    for (int j = 0;j<array_datos.size();j++)
    {
    if(array_datos.get(j).indexOf(te.getText().toString().toUpperCase())!=-1)
    {
    array_busqueda.add(array_datos.get(j));
    }

    }

    index of devuelve -1 si no encuentra la cadena buscada, por lo tanto cualquier otro resultado indicaria que encontro la subcadena

    saludos

  • http://gravatar.com/4rebin rebin

    Thank you very much for this article that is help full for me :)

  • Ivan

    Hola, como sería cuando está la lista filtrada para asignar a cada fila una acción.o un click para que me pase a otra pantalla o actividad????
    Podrías ponerme un ejemplo de la estructura

  • http://www.driverlandia.com Guille

    Una pregunta, que tal seria el comportamiento en un listado de supongamos 500 filas ?

  • http://gravatar.com/betho79 betho

    Estimado
    Estoy tratando de hacer este filtro pero no me sale, resulta que la lista de datos las obtengo de un json de php desde una consulta a una bd MYSQL, me puedes ayudar ??

    • Oscar Patricio Caceres

      pudiste solucionarlo ya?

  • caroprese

    he probado casi todo hasta modificar mi adapter, el problema es que lo tengo en baseadapter y no me funciona el filtro

  • Leo Garcia

    Hola sabes que tengo una duda, he hecho algo parecido a este de aca, solo que a cada lugar del listview al darle click me comunica con otra actividad, el problema esta en que cuando busco ese lugar y le doy click me lleva solo a la activity que esta en la posicion 0. ejemplo:
    Tengo un listview con los siguientes datos.

    Casa0— este me conecta con un actvity ubicado en la posicion 0
    Casa1— este posicion1
    Casa2— este posicion2
    Casa3— este posicion3

    Cuando yo filtro un valor supongamos, en el buscar escribo:

    Casa2
    ———–
    Casa2 (Este es el valor que me devuelve, hasta ahi anda genial)

    Luego con le doy click en Casa2.. me envia al activity asociado a la posicion 0 (seria Casa0).
    Y no se como hacer para que me llame al verdadero. Espero me puedan ayudar, se los agradeceria

  • Mario German Agudelo

    Gracias por compartir tu conocimiento con todos, te queria preguntar como puedo colocar un spinner dentro de un RecyclerView, cuentas con algun tutorial o link al respecto, te agradeceria demasiado.