Filtre d'affichage de liste Android

94

J'ai créé une vue de liste dans Android et je souhaite ajouter du texte d'édition au-dessus de la liste et lorsque l'utilisateur saisit du texte, la liste sera filtrée en fonction de l'entrée de l'utilisateur

Quelqu'un peut-il me dire s'il existe un moyen de filtrer l'adaptateur de liste dans Android?

Amira Elsayed Ismail
la source
1
Salut, essayez cet exemple Exemple un et le second Exemple 2 J'ai implémenté le même basé sur ces tutoriels
..
5
La réponse principale ne m'a tout simplement pas fourni suffisamment d'informations. Cette réponse à une question similaire a plus de contexte et était exactement assez d'informations pour me faire trier.
James Skemp
J'utilise la vue du recycleur Je veux filtrer les enregistrements mais j'utilise des boutons personnalisés pour donner une entrée au texte d'édition comme un clavier personnalisé, mais j'obtiens un retard sur le clic rapide du bouton pouvez-vous m'aider à sortir de cela.J'ai un enregistrement en milliers
Sagar

Réponses:

141

Ajoutez un EditText au-dessus de votre liste dans son fichier de mise en page .xml. Et dans votre activité / fragment ..

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) {}
});

La base ici est d'ajouter un OnTextChangeListener à votre texte d'édition et à l'intérieur de sa méthode de rappel, appliquez le filtre à l'adaptateur de votre liste.

ÉDITER

Pour obtenir un filtre sur votre BaseAdapter personnalisé, vous devrez implémenter une interface 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;
    }
}

Dans performFiltering (), vous devez effectuer une comparaison réelle de la requête de recherche avec les valeurs de votre base de données. Il transmettra son résultat à la méthode publishResults () .

Purush Pawar
la source
J'ai créé un adaptateur personnalisé qui étend BaseAdapter et à l'intérieur j'ai défini un vecteur de mon objet qui sera affiché dans la liste, lorsque j'essaye d'utiliser le code ci-dessus, je n'ai pas trouvé la méthode getFilter dans mon adaptateur, alors pourriez-vous s'il vous plaît dites-moi si je dois implémenter une interface ??
Amira Elsayed Ismail
Filtrer les données dans le cas de BaseAdapter est un peu délicat. Vous devrez implémenter une interface filtrable à votre implémentation de BaseAdapter. Vous aurez alors la méthode getFilter () et à l'intérieur, vous devez implémenter deux méthodes de rappel pour travailler avec; void publishResults () et FilterResults performFiltering (contrainte CharSequence).
Purush Pawar
pouvez-vous soutenir avec un exemple simple s'il vous plaît?
Amira Elsayed Ismail
Oui. Vérifiez la section EDIT de ma réponse.
Purush Pawar
1
Je vous suggère de poster une autre question sur SO à ce sujet. Parce que ce n'est pas une bonne façon de continuer à poser des questions différentes dans le même message. Eh bien, en guise d'avertissement, copiez tout d'abord la liste complète dans une autre liste temporaire et dans la méthode onPerformFiltering (), utilisez cette liste temporaire pour la recherche. Cela résoudra votre problème. Et veuillez voter pour et / ou accepter la réponse si cela vous a aidé.
Purush Pawar
9

Implémentez votre adaptateur Filtrable:

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

puis créez votre classe Filter:

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 cette façon, votre adaptateur est filtrable, vous pouvez passer l'élément de filtre au filtre de l'adaptateur et faire le travail. J'espère que cela vous sera utile.

M.Kouchi
la source
1

Au cas où quelqu'un serait toujours intéressé par ce sujet, je trouve que la meilleure approche pour filtrer les listes est de créer une classe de filtre générique et de l'utiliser avec certaines techniques de réflexion / génériques de base contenues dans le package SDK old school de Java. Voici ce que j'ai fait:

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;
}

Et ensuite, la seule chose que vous devez faire est de créer le filtre en tant que classe membre (éventuellement dans le "onCreate" de la vue) en passant la référence de votre adaptateur, votre liste et la méthode à appeler pour le filtrage:

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

La seule chose qui manque maintenant, c'est de remplacer la méthode "getFilter" dans la classe d'adaptateur:

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

Terminé! Vous devriez réussir à filtrer votre liste - Bien sûr, vous devez également implémenter votre algorithme de filtrage de la meilleure façon qui décrit votre besoin, le code ci-dessous n'est qu'un exemple. . J'espère que cela a aidé, prenez garde.

jbrios777
la source
Je ne sais pas à propos d'Android, mais je me souviens qu'on m'a dit d'essayer d'éviter la réflexion si possible en c # car cela demande beaucoup de ressources (je travaille généralement sur des applications mobiles Windows, cela pourrait donc être un problème), est-ce que cela s'applique sur Android? ou la réflexion a-t-elle le même effet que la construction d'une classe réelle sans génériques? Je pensais créer un modèle pour filtrable et ajouter simplement la classe et la méthode utilisées comme paramètres
Cruces
Oui, vous avez raison. La même chose s'applique ici, la réflexion donne un poids au traitement du programme, mais dans ce cas c'est une utilisation très simple de celui-ci, et parce que nous l'utilisons avec une notation générique / modèle, cela aide également le compilateur. Bonne chance!
jbrios777
NB Vous pouvez avoir des problèmes d'obfuscation (dexguard / proguard) si vous utilisez la réflexion.
Alexey