Cómo escribir un filtro personalizado para ListView con ArrayAdapter

Tengo un ListView que está conectado a un ArrayAdapter donde el artista es una clase simple de mina, que tiene solamente un id y un nombre.

Ahora quiero filtrar el ListView para que llame:

artistAdapter.getFilter().filter("bla", new Filter.FilterListener() { public void onFilterComplete(int count) { Log.d(Config.LOG_TAG, "filter complete! count: " + count); // returns 8 Log.d(Config.LOG_TAG, "adapter count: " + artistAdapter.getCount()); // return 1150 } }); 

La primera declaración de depuración imprime un recuento de 8. Esa es la cantidad de corrent para listitems que comienzan con "bla" pero el adaptador no lo obtiene. La segunda instrucción de depuración imprime un conteo de 1150 elementos. Ese es el número completo de elementos de la lista.

De alguna manera, el filtro no le dice al adaptador que ha filtrado los datos subyacentes.

Quiero saber ahora: ¿tengo código algo en mi adaptador por lo que obtiene las actualizaciones desde el filtro? ¿Tengo que escribir un filtro personalizado? ¿Que tengo que hacer?

Actualmente

Me di cuenta de que debería haber estado usando la lista 'originalItems' para construir el nuevo filtro en performFiltering.

Esto solucionará cualquier problema que vea con respecto a cambiar el texto en el filtro. Por ejemplo, usted busca "pan" y luego retrocede a sólo un "B" y debe ver todos los "B". En mi post original no tendrías.

  private class GlycaemicIndexItemAdapter extends ArrayAdapter<GlycaemicIndexItem> { private ArrayList<GlycaemicIndexItem> items; private ArrayList<GlycaemicIndexItem> originalItems = new ArrayList<GlycaemicIndexItem>(); private GlycaemicIndexItemFilter filter; private final Object mLock = new Object(); public GlycaemicIndexItemAdapter(Context context, int textViewResourceId, ArrayList<GlycaemicIndexItem> newItems) { super(context, textViewResourceId, newItems); this.items = newItems; cloneItems(newItems); } protected void cloneItems(ArrayList<GlycaemicIndexItem> items) { for (Iterator iterator = items.iterator(); iterator .hasNext();) { GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next(); originalItems.add(gi); } } @Override public int getCount() { synchronized(mLock) { return items!=null ? items.size() : 0; } @Override public GlycaemicIndexItem getItem(int item) { GlycaemicIndexItem gi = null; synchronized(mLock) { gi = items!=null ? items.get(item) : null; } return gi; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.row, null); } GlycaemicIndexItem i = null; synchronized(mLock) { i = items.get(position); } if (i != null) { TextView tt = (TextView) v.findViewById(R.id.rowText); TextView bt = (TextView) v.findViewById(R.id.rowText2); if (tt != null) { tt.setText("Name: "+i.getName()); } if(bt != null){ bt.setText("GI Value: " + i.getGlycaemicIndex()); } } return v; } /** * Implementing the Filterable interface. */ public Filter getFilter() { if (filter == null) { filter = new GlycaemicIndexItemFilter(); } return filter; } /** * Custom Filter implementation for the items adapter. * */ private class GlycaemicIndexItemFilter extends Filter { protected FilterResults performFiltering(CharSequence prefix) { // Initiate our results object FilterResults results = new FilterResults(); // No prefix is sent to filter by so we're going to send back the original array if (prefix == null || prefix.length() == 0) { synchronized (mLock) { results.values = originalItems; results.count = originalItems.size(); } } else { synchronized(mLock) { // Compare lower case strings String prefixString = prefix.toString().toLowerCase(); final ArrayList<GlycaemicIndexItem> filteredItems = new ArrayList<GlycaemicIndexItem>(); // Local to here so we're not changing actual array final ArrayList<GlycaemicIndexItem> localItems = new ArrayList<GlycaemicIndexItem>(); localItems.addAll(originalItems); final int count = localItems.size(); for (int i = 0; i < count; i++) { final GlycaemicIndexItem item = localItems.get(i); final String itemName = item.getName().toString().toLowerCase(); // First match against the whole, non-splitted value if (itemName.startsWith(prefixString)) { filteredItems.add(item); } else {} /* This is option and taken from the source of ArrayAdapter final String[] words = itemName.split(" "); final int wordCount = words.length; for (int k = 0; k < wordCount; k++) { if (words[k].startsWith(prefixString)) { newItems.add(item); break; } } } */ } // Set and return results.values = filteredItems; results.count = filteredItems.size(); }//end synchronized } return results; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence prefix, FilterResults results) { //noinspection unchecked synchronized(mLock) { final ArrayList<GlycaemicIndexItem> localItems = (ArrayList<GlycaemicIndexItem>) results.values; notifyDataSetChanged(); clear(); //Add the items back in for (Iterator iterator = localItems.iterator(); iterator .hasNext();) { GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next(); add(gi); } }//end synchronized } } } 

Básicamente, estoy construyendo una aplicación de salud y nutrición y una pantalla tendrá una lista de elementos basados ​​en el índice glucémico / glucémico. Quiero que los usuarios puedan escribir y tener el autofiltro de la pantalla. Ahora, si sólo está usando cadenas, obtiene el autofiltrado de forma gratuita. No estoy, sin embargo, tengo mi propia clase personalizada GlycaemicIndexItem que tiene propiedades en él. Tengo que proporcionar mi propio filtrado para asegurarse de que la lista que se utiliza para dibujar en la pantalla se actualiza cuando el usuario escribe.

Actualmente, la pantalla es una ListActivity simple, con un ListView y un EditText (que el usuario escribe). Adjuntaremos un TextWatcher a este EditText para asegurar que seamos notificados de actualizaciones. Esto significa que debería funcionar para todos los dispositivos, independientemente de que el usuario escriba en un teclado duro o blando (tengo un HTC DesireZ y un G1 antiguo).

Aquí está el layout xml para la pantalla / actividad (¿Puede alguien decirme cómo pegar código xml aquí, como cuando intento utilizar el bloque de código xml no se pega / se muestra correctamente, pero interpretado):

De la actividad - giatoz.xml

Como queremos mostrar nuestras filas en estilo personalizado, también tenemos un archivo xml de diseño para la fila en sí: Archivo xml de fila

Aquí está el código de toda la actividad. Extendiéndose de ListActivity, esta clase tiene una clase interna que actúa como el adaptador, que se extiende desde ArrayAdapter. Esto se instancia en la onCreate de la actividad y pasó una simple lista de cadenas por ahora. Preste atención a cómo se crea en las líneas 39-40. Nuestra disposición especial para la fila se pasa adentro con la lista de artículos.

La clave para rellenar las filas personalizadas se encuentra en el método getView del adaptador.

Nuestra clase de adaptador también tiene su propia clase interna llamada GlycaemicIndexItemFilter que hace el trabajo cuando un usuario escribe. Nuestro filtro está vinculado a nuestro EditText en las líneas 43-44 mediante el uso de un TextWatcher y su método afterTextChanged . La línea 47 es la pista de cómo lograr el filtrado. Llamamos filtro, en nuestro objeto de filtro. Nuestro filtro se crea cuando llamamos a getFilter la primera vez, línea 148-149.

  package com.tilleytech.android.myhealthylife; import java.util.ArrayList; import java.util.Iterator; import android.app.ListActivity; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Filter; import android.widget.ListView; import android.widget.TextView; public class GlycaemicIndexAtoZActivity extends ListActivity { /** Called when the activity is first created. */ private GlycaemicIndexItemAdapter giAdapter; private TextWatcher filterTextWatcher; private EditText filterText = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.giatoz); ListView lv = getListView(); lv.setTextFilterEnabled(true); // By using setAdapter method in listview we an add string array in list. ArrayList<GlycaemicIndexItem> list = getListItems(); giAdapter = new GlycaemicIndexItemAdapter(this, R.layout.row, list); giAdapter.notifyDataSetChanged(); setListAdapter(giAdapter); filterText = (EditText)findViewById(R.id.GI_AtoZSearchEditText); filterTextWatcher = new TextWatcher() { public void afterTextChanged(Editable s) { giAdapter.getFilter().filter(s); //Filter from my adapter giAdapter.notifyDataSetChanged(); //Update my view } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } }; filterText.addTextChangedListener(filterTextWatcher); } private ArrayList<GlycaemicIndexItem> getListItems() { ArrayList<GlycaemicIndexItem> result = new ArrayList<GlycaemicIndexItem>(); Resources res = getResources(); //Get our raw strings String[] array = res.getStringArray(R.array.GIList); for (int i = 0; i < array.length; i++) { GlycaemicIndexItem gi = new GlycaemicIndexItem(); gi.setName(array[i]); gi.setGlycaemicIndex(1); result.add(gi); } return result; } private class GlycaemicIndexItemAdapter extends ArrayAdapter<GlycaemicIndexItem> { private ArrayList<GlycaemicIndexItem> items; private ArrayList<GlycaemicIndexItem> originalItems = new ArrayList<GlycaemicIndexItem>(); private GlycaemicIndexItemFilter filter; private final Object mLock = new Object(); public GlycaemicIndexItemAdapter(Context context, int textViewResourceId, ArrayList<GlycaemicIndexItem> newItems) { super(context, textViewResourceId, newItems); this.items = newItems; cloneItems(newItems); } protected void cloneItems(ArrayList<GlycaemicIndexItem> items) { for (Iterator iterator = items.iterator(); iterator .hasNext();) { GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next(); originalItems.add(gi); } } @Override public int getCount() { synchronized(mLock) { return items!=null ? items.size() : 0; } } @Override public GlycaemicIndexItem getItem(int item) { GlycaemicIndexItem gi = null; synchronized(mLock) { gi = items!=null ? items.get(item) : null; } return gi; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.row, null); } GlycaemicIndexItem i = null; synchronized(mLock) { i = items.get(position); } if (i != null) { TextView tt = (TextView) v.findViewById(R.id.rowText); TextView bt = (TextView) v.findViewById(R.id.rowText2); if (tt != null) { tt.setText("Name: "+i.getName()); } if(bt != null){ bt.setText("GI Value: " + i.getGlycaemicIndex()); } } return v; } /** * Implementing the Filterable interface. */ public Filter getFilter() { if (filter == null) { filter = new GlycaemicIndexItemFilter(); } return filter; } /** * Custom Filter implementation for the items adapter. * */ private class GlycaemicIndexItemFilter extends Filter { protected FilterResults performFiltering(CharSequence prefix) { // Initiate our results object FilterResults results = new FilterResults(); // No prefix is sent to filter by so we're going to send back the original array if (prefix == null || prefix.length() == 0) { synchronized (mLock) { results.values = originalItems; results.count = originalItems.size(); } } else { synchronized(mLock) { // Compare lower case strings String prefixString = prefix.toString().toLowerCase(); final ArrayList<GlycaemicIndexItem> filteredItems = new ArrayList<GlycaemicIndexItem>(); // Local to here so we're not changing actual array final ArrayList<GlycaemicIndexItem> localItems = new ArrayList<GlycaemicIndexItem>(); localItems.addAll(originalItems); final int count = localItems.size(); for (int i = 0; i < count; i++) { final GlycaemicIndexItem item = localItems.get(i); final String itemName = item.getName().toString().toLowerCase(); // First match against the whole, non-splitted value if (itemName.startsWith(prefixString)) { filteredItems.add(item); } else {} /* This is option and taken from the source of ArrayAdapter final String[] words = itemName.split(" "); final int wordCount = words.length; for (int k = 0; k < wordCount; k++) { if (words[k].startsWith(prefixString)) { newItems.add(item); break; } } } */ } // Set and return results.values = filteredItems; results.count = filteredItems.size(); }//end synchronized } return results; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence prefix, FilterResults results) { //noinspection unchecked synchronized(mLock) { final ArrayList<GlycaemicIndexItem> localItems = (ArrayList<GlycaemicIndexItem>) results.values; notifyDataSetChanged(); clear(); //Add the items back in for (Iterator iterator = localItems.iterator(); iterator .hasNext();) { GlycaemicIndexItem gi = (GlycaemicIndexItem) iterator.next(); add(gi); } }//end synchronized } } } } 

Creo que puede utilizar notifyDataSetChanged(); En el método onFilterComplete.

  • Elemento de altura fija de Android ListView
  • La adición de una vista crea un ListView dentro de un fragmento para actualizar
  • ¿Hay una manera de desactivar / editar el desvanecimiento que una vista de lista tiene en sus bordes?
  • ¿Qué adaptador usar - BaseAdapter o ArrayAdapter?
  • Obtener un elemento seleccionado en listview cuando genero el menú contextual
  • Encabezados ListView de Android
  • ¿Qué es android.R.layout.simple_list_item_1?
  • Las extensiones de vínculo se pierden de la conversión de ListView
  • ¿Hay una manera de desactivar el ajuste de línea en una vista de lista en android?
  • Mejores prácticas que combinan vistas de lista y no de lista, como el Mercado
  • Cómo detectar si un ListView se desplaza rápidamente
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.