So filtern Sie eine RecyclerView mit einer SearchView

319

Ich versuche das SearchViewaus der Support-Bibliothek zu implementieren . Ich möchte, dass der Benutzer das verwendet SearchView, um einen Listvon Filmen in einem zu filtern RecyclerView.

Ich habe bisher ein paar Tutorials gefolgt , und ich habe die hinzugefügt , SearchViewum das ActionBar, aber ich bin nicht wirklich sicher , wohin man von hier zu gehen. Ich habe einige Beispiele gesehen, aber keines zeigt Ergebnisse, wenn Sie mit der Eingabe beginnen.

Das ist mein MainActivity:

public class MainActivity extends ActionBarActivity {

    RecyclerView mRecyclerView;
    RecyclerView.LayoutManager mLayoutManager;
    RecyclerView.Adapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new CardAdapter() {
            @Override
            public Filter getFilter() {
                return null;
            }
        };
        mRecyclerView.setAdapter(mAdapter);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Und das ist mein Adapter:

public abstract class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> implements Filterable {

    List<Movie> mItems;

    public CardAdapter() {
        super();
        mItems = new ArrayList<Movie>();
        Movie movie = new Movie();
        movie.setName("Spiderman");
        movie.setRating("92");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Doom 3");
        movie.setRating("91");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers");
        movie.setRating("88");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers 2");
        movie.setRating("87");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Transformers 3");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Noah");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman 2");
        movie.setRating("86");
        mItems.add(movie);

        movie = new Movie();
        movie.setName("Ironman 3");
        movie.setRating("86");
        mItems.add(movie);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_card_item, viewGroup, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        Movie movie = mItems.get(i);
        viewHolder.tvMovie.setText(movie.getName());
        viewHolder.tvMovieRating.setText(movie.getRating());
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{

        public TextView tvMovie;
        public TextView tvMovieRating;

        public ViewHolder(View itemView) {
            super(itemView);
            tvMovie = (TextView)itemView.findViewById(R.id.movieName);
            tvMovieRating = (TextView)itemView.findViewById(R.id.movieRating);
        }
    }
}
Jacques Krause
quelle

Antworten:

913

Einführung

Da aus Ihrer Frage nicht klar hervorgeht, mit was genau Sie Probleme haben, habe ich diese kurze Anleitung zur Implementierung dieser Funktion verfasst. Wenn Sie noch Fragen haben, können Sie diese gerne stellen.

Ich habe ein funktionierendes Beispiel für alles, worüber ich hier in diesem GitHub-Repository spreche .
Wenn Sie mehr über das Beispielprojekt erfahren möchten, besuchen Sie die Projekthomepage .

In jedem Fall sollte das Ergebnis ungefähr so ​​aussehen:

Demo-Bild

Wenn Sie zuerst mit der Demo-App herumspielen möchten, können Sie sie im Play Store installieren:

Bekomm es auf Google Play

Wie auch immer, fangen wir an.


Einrichten der SearchView

res/menuErstellen Sie im Ordner eine neue Datei mit dem Namen main_menu.xml. Fügen Sie darin ein Element hinzu und setzen Sie das actionViewClassauf android.support.v7.widget.SearchView. Da Sie die Support-Bibliothek verwenden, müssen Sie den Namespace der Support-Bibliothek verwenden, um das actionViewClassAttribut festzulegen. Ihre XML-Datei sollte ungefähr so ​​aussehen:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          app:actionViewClass="android.support.v7.widget.SearchView"
          app:showAsAction="always"/>

</menu>

In Ihrem Fragmentoder ActivitySie müssen dieses Menü xml wie gewohnt aufblasen, dann können Sie nach dem suchen, das MenuItemdas enthält, SearchViewund das implementieren, OnQueryTextListenerdas wir verwenden werden, um auf Änderungen des in das eingegebenen Textes zu warten SearchView:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);

    final MenuItem searchItem = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) searchItem.getActionView();
    searchView.setOnQueryTextListener(this);

    return true;
}

@Override
public boolean onQueryTextChange(String query) {
    // Here is where we are going to implement the filter logic
    return false;
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

Und jetzt SearchViewist das gebrauchsfertig. Wir werden die Filterlogik später onQueryTextChange()implementieren, sobald wir die Implementierung der abgeschlossen haben Adapter.


Einrichten der Adapter

In erster Linie ist dies die Modellklasse, die ich für dieses Beispiel verwenden werde:

public class ExampleModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }
}

Es ist nur Ihr Grundmodell, das einen Text in der anzeigt RecyclerView. Dies ist das Layout, mit dem ich den Text anzeigen werde:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="model"
            type="com.github.wrdlbrnft.searchablerecyclerviewdemo.ui.models.ExampleModel"/>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:clickable="true">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="@{model.text}"/>

    </FrameLayout>

</layout>

Wie Sie sehen können, verwende ich die Datenbindung. Wenn Sie noch nie mit Datenbindung gearbeitet haben, lassen Sie sich nicht entmutigen! Es ist sehr einfach und mächtig, aber ich kann nicht erklären, wie es im Rahmen dieser Antwort funktioniert.

Dies ist das ViewHolderfür die ExampleModelKlasse:

public class ExampleViewHolder extends RecyclerView.ViewHolder {

    private final ItemExampleBinding mBinding;

    public ExampleViewHolder(ItemExampleBinding binding) {
        super(binding.getRoot());
        mBinding = binding;
    }

    public void bind(ExampleModel item) {
        mBinding.setModel(item);
    }
}

Wieder nichts Besonderes. Es wird lediglich die Datenbindung verwendet, um die Modellklasse an dieses Layout zu binden, wie wir es in der obigen Layout-XML definiert haben.

Jetzt können wir endlich zu dem wirklich interessanten Teil kommen: Schreiben des Adapters. Ich werde die grundlegende Implementierung von überspringen Adapterund mich stattdessen auf die Teile konzentrieren, die für diese Antwort relevant sind.

Aber zuerst müssen wir über eines sprechen: Die SortedListKlasse.


SortedList

Das SortedListist ein völlig erstaunliches Werkzeug, das Teil der RecyclerViewBibliothek ist. Es kümmert sich um die Benachrichtigung Adapterüber Änderungen am Datensatz und tut dies auf sehr effiziente Weise. Sie müssen lediglich eine Reihenfolge der Elemente angeben. Sie müssen dies tun, indem Sie eine compare()Methode implementieren , die zwei Elemente SortedListwie a vergleicht Comparator. Aber anstatt a Listzu sortieren, werden die Elemente im RecyclerView!

Das SortedListinteragiert mit dem Adapterdurch eine CallbackKlasse, die Sie implementieren müssen:

private final SortedList.Callback<ExampleModel> mCallback = new SortedList.Callback<ExampleModel>() {

    @Override
    public void onInserted(int position, int count) {
         mAdapter.notifyItemRangeInserted(position, count);
    }

    @Override
    public void onRemoved(int position, int count) {
        mAdapter.notifyItemRangeRemoved(position, count);
    }

    @Override
    public void onMoved(int fromPosition, int toPosition) {
        mAdapter.notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onChanged(int position, int count) {
        mAdapter.notifyItemRangeChanged(position, count);
    }

    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return mComparator.compare(a, b);
    }

    @Override
    public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
        return oldItem.equals(newItem);
    }

    @Override
    public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
        return item1.getId() == item2.getId();
    }
}

Bei den Verfahren an der Spitze des Callback wie onMoved, onInsertedetc. müssen Sie rufen die entsprechende Methode Ihrer benachrichtigen Adapter. Die drei Methoden unten compare, areContentsTheSameund areItemsTheSameSie müssen implementieren, je nachdem, welche Art von Objekten Sie anzeigen möchten und in welcher Reihenfolge diese Objekte auf dem Bildschirm angezeigt werden sollen.

Lassen Sie uns diese Methoden einzeln durchgehen:

@Override
public int compare(ExampleModel a, ExampleModel b) {
    return mComparator.compare(a, b);
}

Dies ist die compare()Methode, über die ich zuvor gesprochen habe. In diesem Beispiel leite ich den Anruf nur an a weiter, Comparatordas die beiden Modelle vergleicht. Wenn Sie möchten, dass die Elemente in alphabetischer Reihenfolge auf dem Bildschirm angezeigt werden. Dieser Komparator könnte folgendermaßen aussehen:

private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return a.getText().compareTo(b.getText());
    }
};

Schauen wir uns nun die nächste Methode an:

@Override
public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
    return oldItem.equals(newItem);
}

Mit dieser Methode soll festgestellt werden, ob sich der Inhalt eines Modells geändert hat. Der SortedListverwendet dies, um zu bestimmen, ob ein Änderungsereignis aufgerufen werden muss - mit anderen Worten, ob RecyclerViewdie alte und die neue Version überblendet werden sollen. Wenn Sie Modellklassen korrekt equals()und hashCode()implementiert haben, können Sie diese normalerweise wie oben beschrieben implementieren. Wenn wir der Klasse eine equals()und hashCode()Implementierung hinzufügen ExampleModel, sollte sie ungefähr so ​​aussehen:

public class ExampleModel implements SortedListAdapter.ViewModel {

    private final long mId;
    private final String mText;

    public ExampleModel(long id, String text) {
        mId = id;
        mText = text;
    }

    public long getId() {
        return mId;
    }

    public String getText() {
        return mText;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ExampleModel model = (ExampleModel) o;

        if (mId != model.mId) return false;
        return mText != null ? mText.equals(model.mText) : model.mText == null;

    }

    @Override
    public int hashCode() {
        int result = (int) (mId ^ (mId >>> 32));
        result = 31 * result + (mText != null ? mText.hashCode() : 0);
        return result;
    }
}

Kurze Randnotiz: Die meisten IDEs wie Android Studio, IntelliJ und Eclipse verfügen über Funktionen zum Generieren equals()und hashCode()Implementieren auf Knopfdruck für Sie! Sie müssen sie also nicht selbst implementieren. Schauen Sie im Internet nach, wie es in Ihrer IDE funktioniert!

Schauen wir uns nun die letzte Methode an:

@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
    return item1.getId() == item2.getId();
}

Mit SortedListdieser Methode wird überprüft, ob sich zwei Elemente auf dasselbe beziehen. Im einfachsten Sinne (ohne zu erklären, wie das SortedListfunktioniert) wird dies verwendet, um festzustellen, ob ein Objekt bereits in der ListAnimation enthalten ist und ob entweder eine Animation zum Hinzufügen, Verschieben oder Ändern abgespielt werden muss. Wenn Ihre Modelle eine ID haben, vergleichen Sie bei dieser Methode normalerweise nur die ID. Wenn dies nicht der Fall ist, müssen Sie einen anderen Weg finden, um dies zu überprüfen. Die Implementierung hängt jedoch von Ihrer spezifischen App ab. Normalerweise ist es die einfachste Option, allen Modellen eine ID zuzuweisen. Dies kann beispielsweise das Primärschlüsselfeld sein, wenn Sie die Daten aus einer Datenbank abfragen.

Mit der SortedList.Callbackkorrekt implementierten können wir eine Instanz der folgenden erstellen SortedList:

final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);

Als ersten Parameter im Konstruktor von müssen SortedListSie die Klasse Ihrer Modelle übergeben. Der andere Parameter ist nur der SortedList.Callbackoben definierte.

Kommen wir nun zur Sache: Wenn wir das Adaptermit einem implementieren SortedList, sollte es ungefähr so ​​aussehen:

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final SortedList<ExampleModel> mSortedList = new SortedList<>(ExampleModel.class, new SortedList.Callback<ExampleModel>() {
        @Override
        public int compare(ExampleModel a, ExampleModel b) {
            return mComparator.compare(a, b);
        }

        @Override
        public void onInserted(int position, int count) {
            notifyItemRangeInserted(position, count);
        }

        @Override
        public void onRemoved(int position, int count) {
            notifyItemRangeRemoved(position, count);
        }

        @Override
        public void onMoved(int fromPosition, int toPosition) {
            notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onChanged(int position, int count) {
            notifyItemRangeChanged(position, count);
        }

        @Override
        public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }

        @Override
        public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1.getId() == item2.getId();
        }
    });

    private final LayoutInflater mInflater;
    private final Comparator<ExampleModel> mComparator;

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        mInflater = LayoutInflater.from(context);
        mComparator = comparator;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(inflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mSortedList.get(position);
        holder.bind(model);
    }

    @Override
    public int getItemCount() {
        return mSortedList.size();
    }
}

Das Comparatorzum Sortieren des Elements verwendete Element wird über den Konstruktor übergeben, sodass wir dasselbe verwenden können, Adapterauch wenn die Elemente in einer anderen Reihenfolge angezeigt werden sollen.

Jetzt sind wir fast fertig! Aber wir brauchen zuerst eine Möglichkeit, Elemente zum oder hinzuzufügen Adapter. Zu diesem Zweck können wir Methoden hinzufügen, mit Adapterdenen wir Elemente hinzufügen und entfernen können SortedList:

public void add(ExampleModel model) {
    mSortedList.add(model);
}

public void remove(ExampleModel model) {
    mSortedList.remove(model);
}

public void add(List<ExampleModel> models) {
    mSortedList.addAll(models);
}

public void remove(List<ExampleModel> models) {
    mSortedList.beginBatchedUpdates();
    for (ExampleModel model : models) {
        mSortedList.remove(model);
    }
    mSortedList.endBatchedUpdates();
}

Wir müssen hier keine Benachrichtigungsmethoden aufrufen, da dies SortedListbereits durch das SortedList.Callback! Abgesehen davon ist die Implementierung dieser Methoden mit einer Ausnahme ziemlich einfach: der Methode remove, mit der ein ListModell entfernt wird. Da SortedListes nur eine Methode zum Entfernen gibt, mit der ein einzelnes Objekt entfernt werden kann, müssen wir die Liste durchlaufen und die Modelle einzeln entfernen. Wenn Sie beginBatchedUpdates()zu Beginn aufrufen, werden alle Änderungen zusammengefasst, die wir SortedListgemeinsam vornehmen werden, und die Leistung wird verbessert. Wenn wir rufen endBatchedUpdates()die RecyclerViewüber alle Änderungen sofort benachrichtigt wird.

Außerdem müssen Sie verstehen, dass ein Objekt, das dem hinzugefügt wird SortedListund sich bereits in dem befindet SortedList, nicht erneut hinzugefügt wird. Stattdessen SortedListverwendet der die areContentsTheSame()Methode, um herauszufinden, ob sich das Objekt geändert hat - und ob das Element in dem Objekt RecyclerViewaktualisiert wird.

Was ich normalerweise bevorzuge, ist eine Methode, mit der ich alle Elemente RecyclerViewauf einmal ersetzen kann . Entfernen Sie alles, was nicht im enthalten ist, Listund fügen Sie alle Elemente hinzu, die im SortedList:

public void replaceAll(List<ExampleModel> models) {
    mSortedList.beginBatchedUpdates();
    for (int i = mSortedList.size() - 1; i >= 0; i--) {
        final ExampleModel model = mSortedList.get(i);
        if (!models.contains(model)) {
            mSortedList.remove(model);
        }
    }
    mSortedList.addAll(models);
    mSortedList.endBatchedUpdates();
}

Diese Methode stapelt erneut alle Aktualisierungen, um die Leistung zu steigern. Die erste Schleife ist umgekehrt, da das Entfernen eines Elements am Anfang die Indizes aller darauf folgenden Elemente durcheinander bringen würde und dies in einigen Fällen zu Problemen wie Dateninkonsistenzen führen kann. Danach fügen wir einfach Listdas SortedListusing hinzu addAll(), um alle Elemente hinzuzufügen, die noch nicht im enthalten sind, SortedListund aktualisieren - wie oben beschrieben - alle Elemente, die bereits im Element enthalten sind, sich SortedListjedoch geändert haben.

Und damit Adapterist das abgeschlossen. Das Ganze sollte ungefähr so ​​aussehen:

public class ExampleAdapter extends RecyclerView.Adapter<ExampleViewHolder> {

    private final SortedList<ExampleModel> mSortedList = new SortedList<>(ExampleModel.class, new SortedList.Callback<ExampleModel>() {
        @Override
        public int compare(ExampleModel a, ExampleModel b) {
            return mComparator.compare(a, b);
        }

        @Override
        public void onInserted(int position, int count) {
            notifyItemRangeInserted(position, count);
        }

        @Override
        public void onRemoved(int position, int count) {
            notifyItemRangeRemoved(position, count);
        }

        @Override
        public void onMoved(int fromPosition, int toPosition) {
            notifyItemMoved(fromPosition, toPosition);
        }

        @Override
        public void onChanged(int position, int count) {
            notifyItemRangeChanged(position, count);
        }

        @Override
        public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
            return oldItem.equals(newItem);
        }

        @Override
        public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
            return item1 == item2;
        }
    });

    private final Comparator<ExampleModel> mComparator;
    private final LayoutInflater mInflater;

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        mInflater = LayoutInflater.from(context);
        mComparator = comparator;
    }

    @Override
    public ExampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(mInflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ExampleViewHolder holder, int position) {
        final ExampleModel model = mSortedList.get(position);
        holder.bind(model);
    }

    public void add(ExampleModel model) {
        mSortedList.add(model);
    }

    public void remove(ExampleModel model) {
        mSortedList.remove(model);
    }

    public void add(List<ExampleModel> models) {
        mSortedList.addAll(models);
    }

    public void remove(List<ExampleModel> models) {
        mSortedList.beginBatchedUpdates();
        for (ExampleModel model : models) {
            mSortedList.remove(model);
        }
        mSortedList.endBatchedUpdates();
    }

    public void replaceAll(List<ExampleModel> models) {
        mSortedList.beginBatchedUpdates();
        for (int i = mSortedList.size() - 1; i >= 0; i--) {
            final ExampleModel model = mSortedList.get(i);
            if (!models.contains(model)) {
                mSortedList.remove(model);
            }
        }
        mSortedList.addAll(models);
        mSortedList.endBatchedUpdates();
    }

    @Override
    public int getItemCount() {
        return mSortedList.size();
    }
}

Jetzt fehlt nur noch die Filterung!


Implementierung der Filterlogik

Um die Filterlogik zu implementieren, müssen wir zunächst eines Listaller möglichen Modelle definieren. Für dieses Beispiel ich eine erstellen Listvon ExampleModelInstanzen aus einer Reihe von Filmen:

private static final String[] MOVIES = new String[]{
        ...
};

private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
    @Override
    public int compare(ExampleModel a, ExampleModel b) {
        return a.getText().compareTo(b.getText());
    }
};

private ExampleAdapter mAdapter;
private List<ExampleModel> mModels;
private RecyclerView mRecyclerView;

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    mAdapter = new ExampleAdapter(this, ALPHABETICAL_COMPARATOR);

    mBinding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
    mBinding.recyclerView.setAdapter(mAdapter);

    mModels = new ArrayList<>();
    for (String movie : MOVIES) {
        mModels.add(new ExampleModel(movie));
    }
    mAdapter.add(mModels);
}

Hier ist nichts Besonderes los, wir instanziieren einfach das Adapterund setzen es auf das RecyclerView. Danach erstellen wir eine Reihe Listvon Modellen aus den Filmnamen im MOVIESArray. Dann fügen wir alle Modelle zum hinzu SortedList.

Jetzt können wir zu onQueryTextChange()dem zurückkehren, was wir zuvor definiert haben, und mit der Implementierung der Filterlogik beginnen:

@Override
public boolean onQueryTextChange(String query) {
    final List<ExampleModel> filteredModelList = filter(mModels, query);
    mAdapter.replaceAll(filteredModelList);
    mBinding.recyclerView.scrollToPosition(0);
    return true;
}

Das ist wieder ziemlich einfach. Wir rufen die Methode auf filter()und übergeben das Listof ExampleModels sowie die Abfragezeichenfolge. Wir rufen dann die replaceAll()an Adapterund übergeben die gefilterte ListRückgabe von filter(). Wir müssen uns auch scrollToPosition(0)an die RecyclerViewwenden, um sicherzustellen, dass der Benutzer bei der Suche nach etwas immer alle Elemente sehen kann. Andernfalls RecyclerViewbleibt der beim Filtern möglicherweise in einer nach unten gescrollten Position und blendet anschließend einige Elemente aus. Ein Bildlauf nach oben sorgt für eine bessere Benutzererfahrung bei der Suche.

Jetzt müssen Sie sich nur noch filter()selbst implementieren :

private static List<ExampleModel> filter(List<ExampleModel> models, String query) {
    final String lowerCaseQuery = query.toLowerCase();

    final List<ExampleModel> filteredModelList = new ArrayList<>();
    for (ExampleModel model : models) {
        final String text = model.getText().toLowerCase();
        if (text.contains(lowerCaseQuery)) {
            filteredModelList.add(model);
        }
    }
    return filteredModelList;
}

Als erstes rufen wir hier toLowerCase()die Abfragezeichenfolge auf. Wir möchten nicht, dass bei unserer Suchfunktion zwischen Groß- und Kleinschreibung unterschieden wird. Durch Aufrufen toLowerCase()aller von uns verglichenen Zeichenfolgen können wir sicherstellen, dass unabhängig von der Groß- und Kleinschreibung dieselben Ergebnisse zurückgegeben werden. Es durchläuft dann einfach alle Modelle in dem Modell, das Listwir übergeben haben, und prüft, ob die Abfragezeichenfolge im Text des Modells enthalten ist. Wenn dies der Fall ist, wird das Modell dem gefilterten Modell hinzugefügt List.

Und das ist es! Der obige Code läuft auf API-Level 7 und höher und ab API-Level 11 erhalten Sie kostenlos Elementanimationen!

Mir ist klar, dass dies eine sehr detaillierte Beschreibung ist, die das Ganze wahrscheinlich komplizierter erscheinen lässt als es wirklich ist, aber es gibt eine Möglichkeit, dieses ganze Problem zu verallgemeinern und die Implementierung eines Adapterauf einer Basis SortedListviel einfacher zu machen.


Verallgemeinern des Problems und Vereinfachen des Adapters

In diesem Abschnitt werde ich sehr ins Detail nicht gehen - zum Teil , weil ich nach Antworten auf Stack - Überlauf gegen die normale Zeichenbegrenzung leite, sondern auch , weil die meisten es bereits oben erläuterten - aber die Änderungen zusammenfassen: Wir können eine Basis implementiert AdapterKlasse Dies kümmert sich bereits um den Umgang mit den SortedListsowie die Bindung von Modellen an ViewHolderInstanzen und bietet eine bequeme Möglichkeit, ein Adapterbasierend auf a zu implementieren SortedList. Dafür müssen wir zwei Dinge tun:

  • Wir müssen eine ViewModelSchnittstelle erstellen , die alle Modellklassen implementieren müssen
  • Wir müssen eine ViewHolderUnterklasse erstellen, die eine bind()Methode definiert , mit der AdapterModelle automatisch gebunden werden können.

Dies ermöglicht es uns, uns nur auf den Inhalt zu konzentrieren, der in der angezeigt werden soll, RecyclerViewindem wir nur die Modelle und die entsprechenden ViewHolderImplementierungen implementieren. Mit dieser Basisklasse müssen wir uns nicht um die komplizierten Details von Adapterund kümmern SortedList.

SortedListAdapter

Aufgrund der Zeichenbeschränkung für Antworten in StackOverflow kann ich nicht jeden Schritt der Implementierung dieser Basisklasse ausführen oder sogar den vollständigen Quellcode hier hinzufügen, aber Sie finden den vollständigen Quellcode dieser Basisklasse - ich habe es genannt SortedListAdapter- darin GitHub Gist .

Um Ihnen das Leben zu erleichtern, habe ich auf jCenter eine Bibliothek veröffentlicht, die das SortedListAdapter! Wenn Sie es verwenden möchten, müssen Sie diese Abhängigkeit lediglich zur build.gradle-Datei Ihrer App hinzufügen:

compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'

Weitere Informationen zu dieser Bibliothek finden Sie auf der Homepage der Bibliothek .

Verwenden des SortedListAdapter

Um das zu verwenden, müssen SortedListAdapterwir zwei Änderungen vornehmen:

  • Ändern Sie das ViewHolderso, dass es sich ausdehnt SortedListAdapter.ViewHolder. Der Typparameter sollte das Modell sein, das daran gebunden sein soll ViewHolder- in diesem Fall ExampleModel. Sie müssen performBind()stattdessen Daten an Ihre Modelle binden bind().

    public class ExampleViewHolder extends SortedListAdapter.ViewHolder<ExampleModel> {
    
        private final ItemExampleBinding mBinding;
    
        public ExampleViewHolder(ItemExampleBinding binding) {
            super(binding.getRoot());
            mBinding = binding;
        }
    
        @Override
        protected void performBind(ExampleModel item) {
            mBinding.setModel(item);
        }
    }
  • Stellen Sie sicher, dass alle Ihre Modelle die ViewModelSchnittstelle implementieren :

    public class ExampleModel implements SortedListAdapter.ViewModel {
        ...
    }

Danach müssen wir nur noch das aktualisieren ExampleAdapter, SortedListAdapterum alles zu erweitern und zu entfernen, was wir nicht mehr brauchen. Der Typparameter sollte der Modelltyp sein, mit dem Sie arbeiten - in diesem Fall ExampleModel. Wenn Sie jedoch mit verschiedenen Modelltypen arbeiten, setzen Sie den Typparameter auf ViewModel.

public class ExampleAdapter extends SortedListAdapter<ExampleModel> {

    public ExampleAdapter(Context context, Comparator<ExampleModel> comparator) {
        super(context, ExampleModel.class, comparator);
    }

    @Override
    protected ViewHolder<? extends ExampleModel> onCreateViewHolder(LayoutInflater inflater, ViewGroup parent, int viewType) {
        final ItemExampleBinding binding = ItemExampleBinding.inflate(inflater, parent, false);
        return new ExampleViewHolder(binding);
    }

    @Override
    protected boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
        return item1.getId() == item2.getId();
    }

    @Override
    protected boolean areItemContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
        return oldItem.equals(newItem);
    }
}

Danach sind wir fertig! Doch eine letzte Sache zu erwähnen: Die SortedListAdapterhaben nicht die gleichen add(), remove()oder replaceAll()Methoden unserer ursprünglichen ExampleAdapterhatte. Es verwendet ein separates EditorObjekt, um die Elemente in der Liste zu ändern, auf die über die edit()Methode zugegriffen werden kann. Wenn Sie also Elemente entfernen oder hinzufügen möchten, die Sie aufrufen müssen, fügen Sie edit()die Elemente in dieser EditorInstanz hinzu und entfernen Sie sie. Wenn Sie fertig sind, rufen Sie commit()sie auf, um die Änderungen auf Folgendes anzuwenden SortedList:

mAdapter.edit()
        .remove(modelToRemove)
        .add(listOfModelsToAdd)
        .commit();

Alle Änderungen, die Sie auf diese Weise vornehmen, werden zur Steigerung der Leistung zusammengefasst. Die replaceAll()Methode, die wir in den obigen Kapiteln implementiert haben, ist auch für dieses EditorObjekt vorhanden:

mAdapter.edit()
        .replaceAll(mModels)
        .commit();

Wenn Sie vergessen anzurufen, werden commit()keine Ihrer Änderungen übernommen!

Xaver Kapeller
quelle
4
@TiagoOliveira Nun, es ist so gemacht, dass es sofort funktioniert: D Die Datenbindung ist eine Hürde für Leute, die damit nicht vertraut sind, aber ich habe sie trotzdem aufgenommen, weil sie erstaunlich ist und ich sie fördern möchte. Aus irgendeinem Grund scheinen nicht viele Leute davon zu wissen ...
Xaver Kapeller
78
Ich habe noch nicht die ganze Antwort gelesen, ich musste meine Lesung irgendwo um die Hälfte unterbrechen, um diesen Kommentar zu schreiben - dies ist eine der besten Antworten, die ich hier auf der SO gefunden habe! Vielen Dank!
Daneejela
16
Ich
Fred
7
+1 nur um uns zu zeigen, dass Datenbindung in Android existiert! Ich habe noch nie davon gehört und werde anscheinend damit anfangen. Vielen Dank
Jorge Casariego
6
Diese Lösung ist lächerlich lang und im Allgemeinen überentwickelt. Gehen Sie für den zweiten.
Enrico Casini
192

Alles was Sie tun müssen, ist eine filterMethode hinzuzufügen in RecyclerView.Adapter:

public void filter(String text) {
    items.clear();
    if(text.isEmpty()){
        items.addAll(itemsCopy);
    } else{
        text = text.toLowerCase();
        for(PhoneBookItem item: itemsCopy){
            if(item.name.toLowerCase().contains(text) || item.phone.toLowerCase().contains(text)){
                items.add(item);
            }
        }
    }
    notifyDataSetChanged();
}

itemsCopywird im Adapterkonstruktor wie initialisiert itemsCopy.addAll(items).

Wenn Sie dies tun, rufen Sie einfach an filtervon OnQueryTextListener:

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String query) {
        adapter.filter(query);
        return true;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        adapter.filter(newText);
        return true;
    }
});

Dies ist ein Beispiel für das Filtern meines Telefonbuchs nach Name und Telefonnummer.

klimat
quelle
10
Ich denke, das sollte die akzeptierte Antwort sein. Es ist einfacher und es funktioniert einfach
Jose_GD
6
Einfach und effizient!
AlxDroidDev
11
Beachten Sie, dass Sie die Animation verlieren, wenn Sie diesem Ansatz anstelle der Antwort von @Xaver Kapeller folgen.
Humazed
23
Ich habe die akzeptierte Antwort nicht ausprobiert, weil sie viel zu lang ist. Diese Antwort funktioniert und ist einfach zu implementieren. Vergessen Sie nicht, "app: actionViewClass =" android.support.v7.widget.SearchView "zu Ihrem Menüpunkt XML
hinzuzufügen
3
Was genau sind Artikel und Artikel hier kopieren?
Lucky_girl
82

Wenn wir @Shruthi Kamoji sauberer folgen, können wir einfach ein Filter verwenden, das dafür gedacht ist:

public abstract class GenericRecycleAdapter<E> extends RecyclerView.Adapter implements Filterable
{
    protected List<E> list;
    protected List<E> originalList;
    protected Context context;

    public GenericRecycleAdapter(Context context,
    List<E> list)
    {
        this.originalList = list;
        this.list = list;
        this.context = context;
    }

    ...

    @Override
    public Filter getFilter() {
        return new Filter() {
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                list = (List<E>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                List<E> filteredResults = null;
                if (constraint.length() == 0) {
                    filteredResults = originalList;
                } else {
                    filteredResults = getFilteredResults(constraint.toString().toLowerCase());
                }

                FilterResults results = new FilterResults();
                results.values = filteredResults;

                return results;
            }
        };
    }

    protected List<E> getFilteredResults(String constraint) {
        List<E> results = new ArrayList<>();

        for (E item : originalList) {
            if (item.getName().toLowerCase().contains(constraint)) {
                results.add(item);
            }
        }
        return results;
    }
} 

Das E hier ist ein generischer Typ. Sie können es mit Ihrer Klasse erweitern:

public class customerAdapter extends GenericRecycleAdapter<CustomerModel>

Oder ändern Sie einfach das E in den gewünschten Typ ( <CustomerModel>zum Beispiel)

Dann aus searchView (dem Widget, das Sie auf menu.xml setzen können):

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String text) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String text) {
        yourAdapter.getFilter().filter(text);
        return true;
    }
});
Sagits
quelle
Ich benutze so etwas! Funktioniert gut und generisches Beispiel!
Mateus
Hallo, wer kann mir Schritt für Schritt dabei helfen: stackoverflow.com/questions/40754174/…
Thorvald Olavsen
Die sauberste Antwort!
Adalpari
4
Dies ist viel besser als die Antwort mit der höchsten Abstimmung, da die Operation für einen Arbeitsthread in der Methode performFiltering ausgeführt wird.
Hmmm
1
Sie weisen jedoch verschiedenen Variablen einen Verweis auf dieselbe Liste zu. Zum Beispiel this.originalList = list; Sie sollten stattdessen addAll verwenden oder die Liste im ArrayList-Konstruktor übergeben
Florian Walther
5

Erstellen Sie einfach zwei Listen im Adapter, eine ursprüngliche und eine temporäre, und implementieren Sie Filterable .

    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                final FilterResults oReturn = new FilterResults();
                final ArrayList<T> results = new ArrayList<>();
                if (origList == null)
                    origList = new ArrayList<>(itemList);
                if (constraint != null && constraint.length() > 0) {
                    if (origList != null && origList.size() > 0) {
                        for (final T cd : origList) {
                            if (cd.getAttributeToSearch().toLowerCase()
                                    .contains(constraint.toString().toLowerCase()))
                                results.add(cd);
                        }
                    }
                    oReturn.values = results;
                    oReturn.count = results.size();//newly Aded by ZA
                } else {
                    oReturn.values = origList;
                    oReturn.count = origList.size();//newly added by ZA
                }
                return oReturn;
            }

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(final CharSequence constraint,
                                          FilterResults results) {
                itemList = new ArrayList<>((ArrayList<T>) results.values);
                // FIXME: 8/16/2017 implement Comparable with sort below
                ///Collections.sort(itemList);
                notifyDataSetChanged();
            }
        };
    }

wo

public GenericBaseAdapter(Context mContext, List<T> itemList) {
        this.mContext = mContext;
        this.itemList = itemList;
        this.origList = itemList;
    }
Zar E Ahmer
quelle
Schöne Lösung. Ich habe zwei Listen erstellt und eine einfache Filtermethode verwendet. Ich kann anscheinend nicht die richtige Adapterposition für einen Artikel an die nächste Aktivität übergeben. Ich würde mich über alle Gedanken oder Ideen freuen, die
AJW
4

Im Adapter:

public void setFilter(List<Channel> newList){
        mChannels = new ArrayList<>();
        mChannels.addAll(newList);
        notifyDataSetChanged();
    }

In Aktivität:

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                newText = newText.toLowerCase();
                ArrayList<Channel> newList = new ArrayList<>();
                for (Channel channel: channels){
                    String channelName = channel.getmChannelName().toLowerCase();
                    if (channelName.contains(newText)){
                        newList.add(channel);
                    }
                }
                mAdapter.setFilter(newList);
                return true;
            }
        });
Firoz Ahmed
quelle
SO EINFACH, SO KURZ. Vielen Dank!! funktioniert.
Alon Shlider
3

Mit Android Architektur - Komponenten durch die Verwendung von Livedata kann dies leicht mit jeder Art von implementiert wird Adapter . Sie müssen lediglich die folgenden Schritte ausführen:

1. Richten Sie Ihre Daten aus der zurückZimmer Datenbank als Livedata wie im Beispiel unten:

@Dao
public interface CustomDAO{

@Query("SELECT * FROM words_table WHERE column LIKE :searchquery")
    public LiveData<List<Word>> searchFor(String searchquery);
}

2. Erstellen Sie ein ViewModel- Objekt, um Ihre Daten live über eine Methode zu aktualisieren, die Ihr DAO und Ihre Benutzeroberfläche verbindet

public class CustomViewModel extends AndroidViewModel {

    private final AppDatabase mAppDatabase;

    public WordListViewModel(@NonNull Application application) {
        super(application);
        this.mAppDatabase = AppDatabase.getInstance(application.getApplicationContext());
    }

    public LiveData<List<Word>> searchQuery(String query) {
        return mAppDatabase.mWordDAO().searchFor(query);
    }

}

3. Rufen Sie Ihre Daten im laufenden Betrieb aus dem ViewModel auf, indem Sie die Abfrage wie folgt über onQueryTextListener übergeben:

Stellen Sie onCreateOptionsMenuIhren Hörer wie folgt ein

searchView.setOnQueryTextListener(onQueryTextListener);

Richten Sie Ihren Abfrage-Listener wie folgt irgendwo in Ihrer SearchActivity-Klasse ein

private android.support.v7.widget.SearchView.OnQueryTextListener onQueryTextListener =
            new android.support.v7.widget.SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    getResults(query);
                    return true;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    getResults(newText);
                    return true;
                }

                private void getResults(String newText) {
                    String queryText = "%" + newText + "%";
                    mCustomViewModel.searchQuery(queryText).observe(
                            SearchResultsActivity.this, new Observer<List<Word>>() {
                                @Override
                                public void onChanged(@Nullable List<Word> words) {
                                    if (words == null) return;
                                    searchAdapter.submitList(words);
                                }
                            });
                }
            };

Hinweis : Die Schritte (1.) und (2.) sind Standardimplementierungen von AAC ViewModel und DAO . Die einzige echte "Magie", die hier stattfindet, ist der OnQueryTextListener, der die Ergebnisse Ihrer Liste dynamisch aktualisiert, wenn sich der Abfragetext ändert.

Wenn Sie weitere Informationen benötigen, zögern Sie bitte nicht zu fragen. Ich hoffe das hat geholfen :).

Panos Gr
quelle
1

Dies ist meine Einstellung zur Erweiterung der @ klimat-Antwort, um die Filteranimation nicht zu verlieren.

public void filter(String query){
    int completeListIndex = 0;
    int filteredListIndex = 0;
    while (completeListIndex < completeList.size()){
        Movie item = completeList.get(completeListIndex);
        if(item.getName().toLowerCase().contains(query)){
            if(filteredListIndex < filteredList.size()) {
                Movie filter = filteredList.get(filteredListIndex);
                if (!item.getName().equals(filter.getName())) {
                    filteredList.add(filteredListIndex, item);
                    notifyItemInserted(filteredListIndex);
                }
            }else{
                filteredList.add(filteredListIndex, item);
                notifyItemInserted(filteredListIndex);
            }
            filteredListIndex++;
        }
        else if(filteredListIndex < filteredList.size()){
            Movie filter = filteredList.get(filteredListIndex);
            if (item.getName().equals(filter.getName())) {
                filteredList.remove(filteredListIndex);
                notifyItemRemoved(filteredListIndex);
            }
        }
        completeListIndex++;
    }
}

Grundsätzlich wird eine vollständige Liste durchsucht und Elemente einzeln zu einer gefilterten Liste hinzugefügt / daraus entfernt.

AhmadF
quelle
0

Ich empfehle, die Lösung von @Xaver Kapeller wie folgt zu ändern, um ein Problem zu vermeiden, nachdem Sie den gesuchten Text gelöscht haben (der Filter hat nicht mehr funktioniert), da die Liste auf der Rückseite des Adapters kleiner als die Filterliste ist und die IndexOutOfBoundsException aufgetreten ist. Der Code muss also wie folgt geändert werden

public void addItem(int position, ExampleModel model) {
    if(position >= mModel.size()) {
        mModel.add(model);
        notifyItemInserted(mModel.size()-1);
    } else {
        mModels.add(position, model);
        notifyItemInserted(position);
    }
}

Und ändern Sie auch die moveItem-Funktionalität

public void moveItem(int fromPosition, int toPosition) {
    final ExampleModel model = mModels.remove(fromPosition);
    if(toPosition >= mModels.size()) {
        mModels.add(model);
        notifyItemMoved(fromPosition, mModels.size()-1);
    } else {
        mModels.add(toPosition, model);
        notifyItemMoved(fromPosition, toPosition); 
    }
}

Hoffe, dass es dir helfen könnte!

toidv
quelle
Das ist überhaupt nicht nötig.
Xaver Kapeller
Wenn Sie dies nicht tun, wird die IndexOutOfBoundsException für die ursprüngliche Antwort ausgeführt. Warum ist dies nicht erforderlich? Willst du ein Protokoll? @ XaverKapeller
toidv
Nein, die Ausnahme tritt nur auf, wenn Sie Adapterfalsch implementieren . Ohne Ihren Code zu sehen, ist das wahrscheinlichste Problem, dass Sie keine Kopie der Liste mit allen Elementen an die übergeben Adapter.
Xaver Kapeller
Das Fehlerprotokoll: W / System.err: java.lang.IndexOutOfBoundsException: Ungültiger Index 36, Größe 35 W / System.err: at java.util.ArrayList.throwIndexOutOfBoundsException (ArrayList.java:255) W / System.err: at java.util.ArrayList.add (ArrayList.java:147) W / System.err: at com.quomodo.inploi.ui.adapter.MultipleSelectFilterAdapter.addItem (MultipleSelectFilterAdapter.java:125) W / System.err: at com .quomodo.inploi.ui.adapter.MultipleSelectFilterAdapter.applyAndAnimateAdditions (MultipleSelectFilterAdapter.java:78)
toidv
Bitte helfen Sie, den Quellcode unten zu überprüfen @XaverKapeller gist.github.com/toidv/fe71dc45169e4138271b52fdb29420c5
toidv
0

Recyclerview mit Suchansicht und Clicklistener

Fügen Sie Ihrem Adapter eine Schnittstelle hinzu.

public interface SelectedUser{

    void selectedUser(UserModel userModel);

}

Implementieren Sie die Schnittstelle in Ihrer Hauptaktivität und überschreiben Sie die Methode. @Override public void selectedUser (UserModel userModel) {

    startActivity(new Intent(MainActivity.this, SelectedUserActivity.class).putExtra("data",userModel));



}

Vollständiges Tutorial und Quellcode: Recyclerview mit Suchansicht und Onclicklistener

Richard Kamere
quelle
-1

Ich habe das gleiche Problem mit dem Link mit einigen Änderungen gelöst. Suchfilter in RecyclerView mit Karten. Ist es überhaupt möglich? (hoffe das hilft).

Hier ist meine Adapterklasse

public class ContactListRecyclerAdapter extends RecyclerView.Adapter<ContactListRecyclerAdapter.ContactViewHolder> implements Filterable {

Context mContext;
ArrayList<Contact> customerList;
ArrayList<Contact> parentCustomerList;


public ContactListRecyclerAdapter(Context context,ArrayList<Contact> customerList)
{
    this.mContext=context;
    this.customerList=customerList;
    if(customerList!=null)
    parentCustomerList=new ArrayList<>(customerList);
}

   // other overrided methods

@Override
public Filter getFilter() {
    return new FilterCustomerSearch(this,parentCustomerList);
}
}

// Filterklasse

import android.widget.Filter;
import java.util.ArrayList;


public class FilterCustomerSearch extends Filter
{
private final ContactListRecyclerAdapter mAdapter;
ArrayList<Contact> contactList;
ArrayList<Contact> filteredList;

public FilterCustomerSearch(ContactListRecyclerAdapter mAdapter,ArrayList<Contact> contactList) {
    this.mAdapter = mAdapter;
    this.contactList=contactList;
    filteredList=new ArrayList<>();
}

@Override
protected FilterResults performFiltering(CharSequence constraint) {
    filteredList.clear();
    final FilterResults results = new FilterResults();

    if (constraint.length() == 0) {
        filteredList.addAll(contactList);
    } else {
        final String filterPattern = constraint.toString().toLowerCase().trim();

        for (final Contact contact : contactList) {
            if (contact.customerName.contains(constraint)) {
                filteredList.add(contact);
            }
            else if (contact.emailId.contains(constraint))
            {
                filteredList.add(contact);

            }
            else if(contact.phoneNumber.contains(constraint))
                filteredList.add(contact);
        }
    }
    results.values = filteredList;
    results.count = filteredList.size();
    return results;
}

@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
    mAdapter.customerList.clear();
    mAdapter.customerList.addAll((ArrayList<Contact>) results.values);
    mAdapter.notifyDataSetChanged();
}

}}

// Aktivitätsklasse

public class HomeCrossFadeActivity extends AppCompatActivity implements View.OnClickListener,OnFragmentInteractionListener,OnTaskCompletedListner
{
Fragment fragment;
 protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_homecrossfadeslidingpane2);CardView mCard;
   setContentView(R.layout.your_main_xml);}
   //other overrided methods
  @Override
   public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.

    MenuInflater inflater = getMenuInflater();
    // Inflate menu to add items to action bar if it is present.
    inflater.inflate(R.menu.menu_customer_view_and_search, menu);
    // Associate searchable configuration with the SearchView
    SearchManager searchManager =
            (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView =
            (SearchView) menu.findItem(R.id.menu_search).getActionView();
    searchView.setQueryHint("Search Customer");
    searchView.setSearchableInfo(
            searchManager.getSearchableInfo(getComponentName()));

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            if(fragment instanceof CustomerDetailsViewWithModifyAndSearch)
                ((CustomerDetailsViewWithModifyAndSearch)fragment).adapter.getFilter().filter(newText);
            return false;
        }
    });



    return true;
}
}

Verwenden Sie in der OnQueryTextChangeListener () -Methode Ihren Adapter. Ich habe es in Fragment umgewandelt, da mein Adapter fragmentiert ist. Sie können den Adapter direkt verwenden, wenn er sich in Ihrer Aktivitätsklasse befindet.

Shruthi Kamoji
quelle