Filtro de vista de lista Android

94

He creado una vista de lista en Android y quiero agregar texto de edición encima de la lista y cuando el usuario ingrese texto, la lista se filtrará de acuerdo con la entrada del usuario

¿Alguien puede decirme por favor si hay una manera de filtrar el adaptador de lista en Android?

Amira Elsayed Ismail
fuente
1
Hola, prueba este ejemplo Ejemplo uno y el segundo Ejemplo 2 He implementado lo mismo basado en estos tutoriales. Espero que esto te ayude
Pragnani
5
La respuesta principal simplemente no me proporcionó suficiente información. Esta respuesta a una pregunta similar tiene más contexto y fue exactamente información suficiente para que me aclarara.
James Skemp
Estoy usando la vista de reciclador Quiero filtrar registros pero estoy usando botones personalizados para ingresar el texto de edición como un teclado personalizado, pero recibo un retraso en el botón de clic rápido, ¿pueden ayudarme a salir de esto? Tengo un registro en miles
Sagar

Respuestas:

141

Agregue un EditText en la parte superior de su vista de lista en su archivo de diseño .xml. Y en tu actividad / fragmento ..

lv = (ListView) findViewById(R.id.list_view);
    inputSearch = (EditText) findViewById(R.id.inputSearch);

// Adding items to listview
adapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.product_name,    products);
lv.setAdapter(adapter);       
inputSearch.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
        // When user changed the Text
        MainActivity.this.adapter.getFilter().filter(cs);
    }

    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { }

    @Override
    public void afterTextChanged(Editable arg0) {}
});

Lo básico aquí es agregar un OnTextChangeListener a su texto de edición y dentro de su método de devolución de llamada, aplique el filtro al adaptador de su listview.

EDITAR

Para obtener un filtro para su BaseAdapter personalizado, necesitará implementar una interfaz filtrable .

class CustomAdapter extends BaseAdapter implements Filterable {

    public View getView(){
    ...
    }
    public Integer getCount()
    {
    ...
    }

    @Override
    public Filter getFilter() {

        Filter filter = new Filter() {

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {

                arrayListNames = (List<String>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {

                FilterResults results = new FilterResults();
                ArrayList<String> FilteredArrayNames = new ArrayList<String>();

                // perform your search here using the searchConstraint String.

                constraint = constraint.toString().toLowerCase();
                for (int i = 0; i < mDatabaseOfNames.size(); i++) {
                    String dataNames = mDatabaseOfNames.get(i);
                    if (dataNames.toLowerCase().startsWith(constraint.toString()))  {
                        FilteredArrayNames.add(dataNames);
                    }
                }

                results.count = FilteredArrayNames.size();
                results.values = FilteredArrayNames;
                Log.e("VALUES", results.values.toString());

                return results;
            }
        };

        return filter;
    }
}

Dentro de performFiltering () necesita hacer una comparación real de la consulta de búsqueda con los valores en su base de datos. Pasará su resultado al método publishResults () .

Purush Pawar
fuente
He creado un adaptador personalizado que extiende BaseAdapter y dentro de él he definido un Vector de mi objeto que se mostrará en la lista, cuando intento usar el código anterior no pude encontrar el método getFilter en mi Adaptador, así que podrías por favor dime si tengo que implementar alguna interfaz ??
Amira Elsayed Ismail
Filtrar los datos en el caso de BaseAdapter es un poco complicado. Deberá implementar la interfaz filtrable en su implementación de BaseAdapter. Luego tendrá el método getFilter () y dentro de él tendrá que implementar dos métodos de devolución de llamada para trabajar; void publishResults () y FilterResults performFiltering (restricción CharSequence).
Purush Pawar
¿Puedes apoyar con un ejemplo simple por favor?
Amira Elsayed Ismail
Si. Consulte la sección EDITAR de mi respuesta.
Purush Pawar
1
Le sugiero que publique otra pregunta sobre SO con respecto a esto. Porque no es una forma adecuada de seguir haciendo diferentes preguntas en la misma publicación. Bueno, como un aviso, simplemente copie la lista de matrices completa en otra lista de matrices temporal primero y dentro del método onPerformFiltering () use esta lista temporal para buscar. Esto resolverá su problema. Y por favor vote y / o acepte la respuesta si le ayudó.
Purush Pawar
9

Implemente su adaptador Filtrable:

public class vJournalAdapter extends ArrayAdapter<JournalModel> implements Filterable{
private ArrayList<JournalModel> items;
private Context mContext;
....

luego crea tu clase de filtro:

private class JournalFilter extends Filter{

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults result = new FilterResults();
        List<JournalModel> allJournals = getAllJournals();
        if(constraint == null || constraint.length() == 0){

            result.values = allJournals;
            result.count = allJournals.size();
        }else{
            ArrayList<JournalModel> filteredList = new ArrayList<JournalModel>();
            for(JournalModel j: allJournals){
                if(j.source.title.contains(constraint))
                    filteredList.add(j);
            }
            result.values = filteredList;
            result.count = filteredList.size();
        }

        return result;
    }
    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        if (results.count == 0) {
            notifyDataSetInvalidated();
        } else {
            items = (ArrayList<JournalModel>) results.values;
            notifyDataSetChanged();
        }
    }

}

De esta manera, su adaptador es filtrable, puede pasar el elemento del filtro al filtro del adaptador y hacer el trabajo. Espero que esto te sea de ayuda.

M.Kouchi
fuente
1

En caso de que alguien todavía esté interesado en este tema, creo que el mejor enfoque para filtrar listas es crear una clase de filtro genérica y usarla con algunas técnicas básicas de reflexión / genéricas contenidas en el paquete SDK de la vieja escuela de Java. Esto es lo que hice:

public class GenericListFilter<T> extends Filter {

    /**
     * Copycat constructor
     * @param list  the original list to be used
     */
    public GenericListFilter (List<T> list, String reflectMethodName, ArrayAdapter<T> adapter) {
        super ();

        mInternalList = new ArrayList<>(list);
        mAdapterUsed  = adapter;

        try {
            ParameterizedType stringListType = (ParameterizedType)
                    getClass().getField("mInternalList").getGenericType();
            mCompairMethod =
                    stringListType.getActualTypeArguments()[0].getClass().getMethod(reflectMethodName);
        }
        catch (Exception ex) {
            Log.w("GenericListFilter", ex.getMessage(), ex);

            try {
                if (mInternalList.size() > 0) {
                    T type = mInternalList.get(0);
                    mCompairMethod = type.getClass().getMethod(reflectMethodName);
                }
            }
            catch (Exception e) {
                Log.e("GenericListFilter", e.getMessage(), e);
            }

        }
    }

    /**
     * Let's filter the data with the given constraint
     * @param constraint
     * @return
     */
    @Override protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults results = new FilterResults();
        List<T> filteredContents = new ArrayList<>();

        if ( constraint.length() > 0 ) {
            try {
                for (T obj : mInternalList) {
                    String result = (String) mCompairMethod.invoke(obj);
                    if (result.toLowerCase().startsWith(constraint.toString().toLowerCase())) {
                        filteredContents.add(obj);
                    }
                }
            }
            catch (Exception ex) {
                Log.e("GenericListFilter", ex.getMessage(), ex);
            }
        }
        else {
            filteredContents.addAll(mInternalList);
        }

        results.values = filteredContents;
        results.count  = filteredContents.size();
        return results;
    }

    /**
     * Publish the filtering adapter list
     * @param constraint
     * @param results
     */
    @Override protected void publishResults(CharSequence constraint, FilterResults results) {
        mAdapterUsed.clear();
        mAdapterUsed.addAll((List<T>) results.values);

        if ( results.count == 0 ) {
            mAdapterUsed.notifyDataSetInvalidated();
        }
        else {
            mAdapterUsed.notifyDataSetChanged();
        }
    }

    // class properties
    private ArrayAdapter<T> mAdapterUsed;
    private List<T> mInternalList;
    private Method  mCompairMethod;
}

Y luego, lo único que debe hacer es crear el filtro como una clase miembro (posiblemente dentro de la vista "onCreate") pasando su referencia de adaptador, su lista y el método que se llamará para el filtrado:

this.mFilter = new GenericFilter<MyObjectBean> (list, "getName", adapter);

Lo único que falta ahora es anular el método "getFilter" en la clase del adaptador:

@Override public Filter getFilter () {
     return MyViewClass.this.mFilter;
}

¡Todo listo! Debe filtrar su lista con éxito: por supuesto, también debe implementar su algoritmo de filtro de la mejor manera que describa su necesidad, el código a continuación es solo un ejemplo. . Espero que haya ayudado, cuídate.

jbrios777
fuente
No sé sobre Android, pero recuerdo que me dijeron que tratara de evitar la reflexión si es posible en c # porque consume muchos recursos (generalmente trabajo en aplicaciones móviles de Windows, por lo que esto podría ser un problema), ¿esto se aplica a Android? ¿O la reflexión tiene el mismo efecto que construir una clase real sin genéricos? Estaba pensando en crear una plantilla para filtrar y simplemente agregar la clase y el método utilizados como parámetros
Cruces
Sí, tienes razón. Lo mismo se aplica aquí, la reflexión le da un peso al procesamiento del programa, pero en este caso es un uso muy simple, y como lo estamos usando con una notación genérica / de plantilla, también ayuda al compilador. ¡Buena suerte!
jbrios777
Nota: puede tener problemas con la ofuscación (dexguard / proguard) si utiliza la reflexión.
Alexey