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:
- Lectura del archivo .txt desde un enlace compartido desde un dropbox Android studio
- ViewPagers en un ListView mostrando elementos de la lista en blanco
- Ocultar y mostrar el botón flotante mientras que los rollos de ListView
- Android listview fila eliminación de animación
- ¿Mi proceso de almacenamiento en caché de imágenes va a perder memoria?
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?
- Android: Adición de texto de sub-artículo de ListView
- Android: nesting FrameLayouts - ¿cuál es la sobrecarga de rendimiento exacto?
- CollapsingToolbarLayout problema con GridView
- ¿Cómo puedo probar ListView con Robolectric 2.2? Dice que android.widget.ListView todavía no está implementado
- Uso de opengl para renderizar cada elemento en un listview en android
- Me gusta saber cómo obtener el elemento seleccionado en la lista expandible ANDROID
- Lista colgadaElector en ListView
- Devolución de llamada a notifyDataSetChanged
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):
Como queremos mostrar nuestras filas en estilo personalizado, también tenemos un archivo xml de diseño para la fila en sí:
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.
- ¿Qué es una clase completa de ayuda de base de datos Android para una base de datos SQLite existente?
- ¿Cómo puedo hacer cumplir una referencia circular en un RelativeLayout?