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:
Wenn Sie zuerst mit der Demo-App herumspielen möchten, können Sie sie im Play Store installieren:
Wie auch immer, fangen wir an.
Einrichten der SearchView
res/menu
Erstellen Sie im Ordner eine neue Datei mit dem Namen main_menu.xml
. Fügen Sie darin ein Element hinzu und setzen Sie das actionViewClass
auf android.support.v7.widget.SearchView
. Da Sie die Support-Bibliothek verwenden, müssen Sie den Namespace der Support-Bibliothek verwenden, um das actionViewClass
Attribut 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 Fragment
oder Activity
Sie müssen dieses Menü xml wie gewohnt aufblasen, dann können Sie nach dem suchen, das MenuItem
das enthält, SearchView
und das implementieren, OnQueryTextListener
das 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 SearchView
ist 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 ViewHolder
für die ExampleModel
Klasse:
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 Adapter
und mich stattdessen auf die Teile konzentrieren, die für diese Antwort relevant sind.
Aber zuerst müssen wir über eines sprechen: Die SortedList
Klasse.
SortedList
Das SortedList
ist ein völlig erstaunliches Werkzeug, das Teil der RecyclerView
Bibliothek 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 SortedList
wie a vergleicht Comparator
. Aber anstatt a List
zu sortieren, werden die Elemente im RecyclerView
!
Das SortedList
interagiert mit dem Adapter
durch eine Callback
Klasse, 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
, onInserted
etc. müssen Sie rufen die entsprechende Methode Ihrer benachrichtigen Adapter
. Die drei Methoden unten compare
, areContentsTheSame
und areItemsTheSame
Sie 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, Comparator
das 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 SortedList
verwendet dies, um zu bestimmen, ob ein Änderungsereignis aufgerufen werden muss - mit anderen Worten, ob RecyclerView
die 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 SortedList
dieser Methode wird überprüft, ob sich zwei Elemente auf dasselbe beziehen. Im einfachsten Sinne (ohne zu erklären, wie das SortedList
funktioniert) wird dies verwendet, um festzustellen, ob ein Objekt bereits in der List
Animation 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.Callback
korrekt 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 SortedList
Sie die Klasse Ihrer Modelle übergeben. Der andere Parameter ist nur der SortedList.Callback
oben definierte.
Kommen wir nun zur Sache: Wenn wir das Adapter
mit 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 Comparator
zum Sortieren des Elements verwendete Element wird über den Konstruktor übergeben, sodass wir dasselbe verwenden können, Adapter
auch 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 Adapter
denen 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 SortedList
bereits durch das SortedList.Callback
! Abgesehen davon ist die Implementierung dieser Methoden mit einer Ausnahme ziemlich einfach: der Methode remove, mit der ein List
Modell entfernt wird. Da SortedList
es 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 SortedList
gemeinsam 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 SortedList
und sich bereits in dem befindet SortedList
, nicht erneut hinzugefügt wird. Stattdessen SortedList
verwendet der die areContentsTheSame()
Methode, um herauszufinden, ob sich das Objekt geändert hat - und ob das Element in dem Objekt RecyclerView
aktualisiert wird.
Was ich normalerweise bevorzuge, ist eine Methode, mit der ich alle Elemente RecyclerView
auf einmal ersetzen kann . Entfernen Sie alles, was nicht im enthalten ist, List
und 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 List
das SortedList
using hinzu addAll()
, um alle Elemente hinzuzufügen, die noch nicht im enthalten sind, SortedList
und aktualisieren - wie oben beschrieben - alle Elemente, die bereits im Element enthalten sind, sich SortedList
jedoch geändert haben.
Und damit Adapter
ist 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 List
aller möglichen Modelle definieren. Für dieses Beispiel ich eine erstellen List
von ExampleModel
Instanzen 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 Adapter
und setzen es auf das RecyclerView
. Danach erstellen wir eine Reihe List
von Modellen aus den Filmnamen im MOVIES
Array. 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 List
of ExampleModel
s sowie die Abfragezeichenfolge. Wir rufen dann die replaceAll()
an Adapter
und übergeben die gefilterte List
Rückgabe von filter()
. Wir müssen uns auch scrollToPosition(0)
an die RecyclerView
wenden, um sicherzustellen, dass der Benutzer bei der Suche nach etwas immer alle Elemente sehen kann. Andernfalls RecyclerView
bleibt 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 List
wir ü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 Adapter
auf einer Basis SortedList
viel 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 Adapter
Klasse Dies kümmert sich bereits um den Umgang mit den SortedList
sowie die Bindung von Modellen an ViewHolder
Instanzen und bietet eine bequeme Möglichkeit, ein Adapter
basierend auf a zu implementieren SortedList
. Dafür müssen wir zwei Dinge tun:
- Wir müssen eine
ViewModel
Schnittstelle erstellen , die alle Modellklassen implementieren müssen
- Wir müssen eine
ViewHolder
Unterklasse erstellen, die eine bind()
Methode definiert , mit der Adapter
Modelle automatisch gebunden werden können.
Dies ermöglicht es uns, uns nur auf den Inhalt zu konzentrieren, der in der angezeigt werden soll, RecyclerView
indem wir nur die Modelle und die entsprechenden ViewHolder
Implementierungen implementieren. Mit dieser Basisklasse müssen wir uns nicht um die komplizierten Details von Adapter
und 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 SortedListAdapter
wir zwei Änderungen vornehmen:
Ändern Sie das ViewHolder
so, 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 ViewModel
Schnittstelle implementieren :
public class ExampleModel implements SortedListAdapter.ViewModel {
...
}
Danach müssen wir nur noch das aktualisieren ExampleAdapter
, SortedListAdapter
um 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 SortedListAdapter
haben nicht die gleichen add()
, remove()
oder replaceAll()
Methoden unserer ursprünglichen ExampleAdapter
hatte. Es verwendet ein separates Editor
Objekt, 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 Editor
Instanz 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 Editor
Objekt vorhanden:
mAdapter.edit()
.replaceAll(mModels)
.commit();
Wenn Sie vergessen anzurufen, werden commit()
keine Ihrer Änderungen übernommen!
Alles was Sie tun müssen, ist eine
filter
Methode hinzuzufügen inRecyclerView.Adapter
:itemsCopy
wird im Adapterkonstruktor wie initialisiertitemsCopy.addAll(items)
.Wenn Sie dies tun, rufen Sie einfach an
filter
vonOnQueryTextListener
:Dies ist ein Beispiel für das Filtern meines Telefonbuchs nach Name und Telefonnummer.
quelle
Wenn wir @Shruthi Kamoji sauberer folgen, können wir einfach ein Filter verwenden, das dafür gedacht ist:
Das E hier ist ein generischer Typ. Sie können es mit Ihrer Klasse erweitern:
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):
quelle
wo
quelle
Im Adapter:
In Aktivität:
quelle
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:
2. Erstellen Sie ein ViewModel- Objekt, um Ihre Daten live über eine Methode zu aktualisieren, die Ihr DAO und Ihre Benutzeroberfläche verbindet
3. Rufen Sie Ihre Daten im laufenden Betrieb aus dem ViewModel auf, indem Sie die Abfrage wie folgt über onQueryTextListener übergeben:
Stellen Sie
onCreateOptionsMenu
Ihren Hörer wie folgt einRichten Sie Ihren Abfrage-Listener wie folgt irgendwo in Ihrer SearchActivity-Klasse ein
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 :).
quelle
Dies ist meine Einstellung zur Erweiterung der @ klimat-Antwort, um die Filteranimation nicht zu verlieren.
Grundsätzlich wird eine vollständige Liste durchsucht und Elemente einzeln zu einer gefilterten Liste hinzugefügt / daraus entfernt.
quelle
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
Und ändern Sie auch die moveItem-Funktionalität
Hoffe, dass es dir helfen könnte!
quelle
Adapter
falsch implementieren . Ohne Ihren Code zu sehen, ist das wahrscheinlichste Problem, dass Sie keine Kopie der Liste mit allen Elementen an die übergebenAdapter
.Fügen Sie Ihrem Adapter eine Schnittstelle hinzu.
Implementieren Sie die Schnittstelle in Ihrer Hauptaktivität und überschreiben Sie die Methode. @Override public void selectedUser (UserModel userModel) {
Vollständiges Tutorial und Quellcode: Recyclerview mit Suchansicht und Onclicklistener
quelle
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
// Filterklasse
}}
// Aktivitätsklasse
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.
quelle