introduction
Puisqu'il n'est pas vraiment clair de votre question avec quoi exactement vous rencontrez des problèmes, j'ai écrit cette procédure pas à pas sur la façon de mettre en œuvre cette fonctionnalité; si vous avez encore des questions, n'hésitez pas à demander.
J'ai un exemple de travail de tout ce dont je parle ici dans ce référentiel GitHub .
Si vous voulez en savoir plus sur l'exemple de projet, visitez la page d'accueil du projet .
Dans tous les cas, le résultat devrait ressembler à ceci:
Si vous souhaitez d'abord jouer avec l'application de démonstration, vous pouvez l'installer à partir du Play Store:
Quoi qu'il en soit, commençons.
Configuration du SearchView
Dans le dossier, res/menu
créez un nouveau fichier appelé main_menu.xml
. Dans ce document, ajoutez un élément et définissez le actionViewClass
sur android.support.v7.widget.SearchView
. Puisque vous utilisez la bibliothèque de support, vous devez utiliser l'espace de noms de la bibliothèque de support pour définir l' actionViewClass
attribut. Votre fichier xml devrait ressembler à ceci:
<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>
Dans votre Fragment
ou Activity
vous devez gonfler ce menu xml comme d'habitude, alors vous pouvez rechercher celui MenuItem
qui contient le SearchView
et implémenter le OnQueryTextListener
que nous allons utiliser pour écouter les modifications du texte entré dans le 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;
}
Et maintenant, le SearchView
est prêt à être utilisé. Nous implémenterons la logique de filtrage plus tard onQueryTextChange()
une fois que nous aurons terminé d'implémenter le Adapter
.
Configuration du Adapter
C'est avant tout la classe modèle que je vais utiliser pour cet exemple:
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;
}
}
C'est juste votre modèle de base qui affichera un texte dans le RecyclerView
. Voici la disposition que je vais utiliser pour afficher le texte:
<?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>
Comme vous pouvez le voir, j'utilise la liaison de données. Si vous n'avez jamais travaillé avec la liaison de données auparavant, ne vous découragez pas! C'est très simple et puissant, mais je ne peux pas expliquer comment cela fonctionne dans le cadre de cette réponse.
C'est ViewHolder
pour la ExampleModel
classe:
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);
}
}
Encore une fois rien de spécial. Il utilise simplement la liaison de données pour lier la classe de modèle à cette disposition comme nous l'avons défini dans le xml de disposition ci-dessus.
Maintenant, nous pouvons enfin arriver à la partie vraiment intéressante: écrire l'adaptateur. Je vais sauter la mise en œuvre de base de laAdapter
et je vais plutôt me concentrer sur les parties qui sont pertinentes pour cette réponse.
Mais d'abord, nous devons parler d'une chose: la SortedList
classe.
SortedList
Le SortedList
est un outil complètement incroyable qui fait partie de la RecyclerView
bibliothèque. Il prend soin de notifier les Adapter
modifications apportées à l'ensemble de données et le fait de manière très efficace. La seule chose qu'il vous oblige à faire est de spécifier un ordre des éléments. Vous devez le faire en implémentant une compare()
méthode qui compare deux éléments dans le SortedList
même que a Comparator
. Mais au lieu de trier un, List
il est utilisé pour trier les éléments dans le RecyclerView
!
Le SortedList
interagit avec le Adapter
via une Callback
classe que vous devez implémenter:
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();
}
}
Dans les méthodes en haut de la fonction de rappel comme onMoved
, onInserted
, etc. , vous devez appeler l'équivalent notify méthode de votre Adapter
. Les trois méthodes en bas compare
, areContentsTheSame
etareItemsTheSame
vous devez implémenter en fonction du type d'objets que vous souhaitez afficher et dans quel ordre ces objets doivent apparaître à l'écran.
Passons en revue ces méthodes une par une:
@Override
public int compare(ExampleModel a, ExampleModel b) {
return mComparator.compare(a, b);
}
C'est la compare()
méthode dont j'ai parlé plus tôt. Dans cet exemple, je passe juste l'appel à un Comparator
qui compare les deux modèles. Si vous souhaitez que les éléments apparaissent par ordre alphabétique à l'écran. Ce comparateur pourrait ressembler à ceci:
private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
@Override
public int compare(ExampleModel a, ExampleModel b) {
return a.getText().compareTo(b.getText());
}
};
Voyons maintenant la méthode suivante:
@Override
public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
return oldItem.equals(newItem);
}
Le but de cette méthode est de déterminer si le contenu d'un modèle a changé. L' SortedList
utilise pour déterminer si un événement de modification doit être appelé - en d'autres termes, si le RecyclerView
fondu enchaîné avec l'ancienne et la nouvelle version. Si vous modélisez les classes avec une implémentation correcte equals()
, hashCode()
vous pouvez généralement l'implémenter comme ci-dessus. Si nous ajoutons une implémentation equals()
et hashCode()
à la ExampleModel
classe, cela devrait ressembler à ceci:
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;
}
}
Note rapide: la plupart des IDE comme Android Studio, IntelliJ et Eclipse ont des fonctionnalités pour générer equals()
et hashCode()
implémenter pour vous en appuyant sur un bouton! Vous n'avez donc pas à les mettre en œuvre vous-même. Regardez sur Internet comment cela fonctionne dans votre IDE!
Voyons maintenant la dernière méthode:
@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
return item1.getId() == item2.getId();
}
Le SortedList
utilise cette méthode pour vérifier si deux éléments font référence à la même chose. En termes plus simples (sans expliquer comment cela SortedList
fonctionne), ceci est utilisé pour déterminer si un objet est déjà contenu dans leList
et si une animation d'ajout, de déplacement ou de changement doit être lue. Si vos modèles ont un identifiant, vous ne comparez généralement que l'identifiant dans cette méthode. S'ils ne le font pas, vous devez trouver un autre moyen de vérifier cela, mais vous finirez par l'implémenter, cela dépend de votre application spécifique. Habituellement, c'est l'option la plus simple de donner à tous les modèles un identifiant - qui pourrait par exemple être le champ de clé primaire si vous interrogez les données d'une base de données.
Avec l' SortedList.Callback
implémentation correcte, nous pouvons créer une instance de laSortedList
:
final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);
En tant que premier paramètre du constructeur du SortedList
vous devez passer la classe de vos modèles. L'autre paramètre est juste celui que SortedList.Callback
nous avons défini ci-dessus.
Passons maintenant aux choses sérieuses: si nous implémentons le Adapter
avec un, SortedList
il devrait ressembler à ceci:
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();
}
}
le Comparator
outil utilisé pour trier l'élément est transmis via le constructeur afin que nous puissions utiliser le même, Adapter
même si les éléments sont censés être affichés dans un ordre différent.
Maintenant, nous avons presque terminé! Mais nous avons d'abord besoin d'un moyen d'ajouter ou de supprimer des éléments dans le Adapter
. À cette fin, nous pouvons ajouter des méthodes à la Adapter
qui nous permettent d'ajouter et de supprimer des éléments à la 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();
}
Nous n'avons pas besoin d'appeler de méthodes de notification ici, car le fait SortedList
déjà cela via le SortedList.Callback
! En dehors de cela, l'implémentation de ces méthodes est assez simple à une exception près: la méthode remove qui supprime un List
des modèles. Étant donné SortedList
que la méthode a une seule suppression qui peut supprimer un seul objet, nous devons parcourir la liste et supprimer les modèles un par un. L'appel beginBatchedUpdates()
au début regroupe tous les changements que nous allons apporter à l' SortedList
ensemble et améliore les performances. Quand on appelleendBatchedUpdates()
le RecyclerView
est informé de tous les changements à la fois.
De plus, ce que vous devez comprendre, c'est que si vous ajoutez un objet au SortedList
et qu'il est déjà dans le, SortedList
il ne sera pas ajouté à nouveau. Au lieu de cela, la méthode SortedList
utilise areContentsTheSame()
pour déterminer si l'objet a changé - et s'il contient l'élément dans le RecyclerView
sera mis à jour.
Quoi qu'il en soit, ce que je préfère habituellement, c'est une méthode qui me permet de remplacer tous les articles en RecyclerView
une seule fois. Supprimez tout ce qui n'est pas dans le List
et ajoutez tous les éléments manquants dans le 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();
}
Cette méthode regroupe à nouveau toutes les mises à jour pour augmenter les performances. La première boucle est inversée car la suppression d'un élément au début gâcherait les index de tous les éléments qui le suivent et cela peut entraîner dans certains cas des problèmes tels que des incohérences de données. Après cela, nous ajoutons simplement le List
à l' SortedList
aide de addAll()
pour ajouter tous les éléments qui ne sont pas déjà dans leSortedList
et - comme je l'ai décrit ci-dessus - mettez à jour tous les éléments qui sont déjà dans le SortedList
mais qui ont changé.
Et avec cela le Adapter
est complet. Le tout devrait ressembler à ceci:
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();
}
}
La seule chose qui manque maintenant est d'implémenter le filtrage!
Implémentation de la logique de filtrage
Pour implémenter la logique de filtrage, nous devons d'abord définir un List
de tous les modèles possibles. Pour cet exemple , je crée une List
des ExampleModel
instances d'un tableau de films:
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);
}
Rien de spécial ne se passe ici, nous instancions juste le Adapter
et le mettons au RecyclerView
. Après cela, nous créons un List
des modèles à partir des noms de films dans le MOVIES
tableau. Ensuite, nous ajoutons tous les modèles au SortedList
.
Maintenant, nous pouvons revenir à onQueryTextChange()
ce que nous avons défini précédemment et commencer à implémenter la logique de filtrage:
@Override
public boolean onQueryTextChange(String query) {
final List<ExampleModel> filteredModelList = filter(mModels, query);
mAdapter.replaceAll(filteredModelList);
mBinding.recyclerView.scrollToPosition(0);
return true;
}
C'est encore une fois assez simple. Nous appelons la méthode filter()
et passons le List
de ExampleModel
s ainsi que la chaîne de requête. Nous appelons ensuite replaceAll()
le Adapter
et passons le filtré List
renvoyé par filter()
. Nous devons également faire appel scrollToPosition(0)
à RecyclerView
pour garantir que l'utilisateur puisse toujours voir tous les éléments lorsqu'il recherche quelque chose. Sinon, le RecyclerView
peut rester dans une position déroulante pendant le filtrage et masquer ensuite quelques éléments. Faire défiler vers le haut garantit une meilleure expérience utilisateur lors de la recherche.
Il ne reste plus qu'à s'implémenter filter()
:
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;
}
La première chose que nous faisons ici est d'appeler toLowerCase()
la chaîne de requête. Nous ne voulons pas que notre fonction de recherche soit sensible à la casse et en appelant toLowerCase()
toutes les chaînes que nous comparons, nous pouvons nous assurer que nous retournons les mêmes résultats indépendamment de la casse. Il réitère ensuite tous les modèles du fichier que List
nous lui avons transmis et vérifie si la chaîne de requête est contenue dans le texte du modèle. Si c'est le cas, le modèle est ajouté au filtre List
.
Et c'est tout! Le code ci-dessus fonctionnera au niveau 7 et supérieur de l'API et à partir du niveau 11 de l'API, vous obtiendrez des animations d'objets gratuitement!
Je me rends compte que c'est une description très détaillée qui rend probablement tout cela plus compliqué qu'il ne l'est vraiment, mais il y a un moyen de généraliser tout ce problème et de rendre l'implémentation d'un Adapter
basé sur SortedList
beaucoup plus simple.
Généraliser le problème et simplifier l'adaptateur
Dans cette section, je ne vais pas entrer dans les détails - en partie parce que je me heurte à la limite de caractères pour les réponses sur Stack Overflow mais aussi parce que la plupart d'entre eux ont déjà été expliqués ci-dessus - mais pour résumer les changements: nous pouvons implémenter une Adapter
classe de base qui prend déjà en charge le traitement SortedList
des modèles ainsi que la liaison aux ViewHolder
instances et fournit un moyen pratique d'implémenter un Adapter
basé sur un SortedList
. Pour cela, nous devons faire deux choses:
- Nous devons créer un
ViewModel
interface que toutes les classes de modèles doivent implémenter
- Nous devons créer une
ViewHolder
sous - classe qui définit une bind()
méthode Adapter
pouvant être utilisée pour lier automatiquement des modèles.
Cela nous permet de nous concentrer uniquement sur le contenu qui est censé être affiché dans le RecyclerView
en implémentant simplement les modèles et les ViewHolder
implémentations correspondantes . En utilisant cette classe de base, nous n'avons pas à nous soucier des détails complexes du Adapter
et de son SortedList
.
SortedListAdapter
En raison de la limite de caractères pour les réponses sur StackOverflow, je ne peux pas passer par chaque étape de l'implémentation de cette classe de base ou même ajouter le code source complet ici, mais vous pouvez trouver le code source complet de cette classe de base - je l'ai appelé SortedListAdapter
- dans ce GitHub Gist .
Pour vous simplifier la vie, j'ai publié une bibliothèque sur jCenter qui contient le SortedListAdapter
! Si vous souhaitez l'utiliser, il vous suffit d'ajouter cette dépendance au fichier build.gradle de votre application:
compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'
Vous pouvez trouver plus d'informations sur cette bibliothèque sur la page d'accueil de la bibliothèque .
Utilisation de SortedListAdapter
Pour utiliser le, SortedListAdapter
nous devons apporter deux modifications:
Modifiez le ViewHolder
pour qu'il s'étende SortedListAdapter.ViewHolder
. Le paramètre type doit être le modèle qui doit y être lié ViewHolder
- dans ce cas ExampleModel
. Vous devez lier des données à vos modèles au performBind()
lieu de 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);
}
}
Assurez-vous que tous vos modèles implémentent l' ViewModel
interface:
public class ExampleModel implements SortedListAdapter.ViewModel {
...
}
Après cela, il nous suffit de mettre à jour le ExampleAdapter
pour étendre SortedListAdapter
et supprimer tout ce dont nous n'avons plus besoin. Le paramètre type doit être le type de modèle avec lequel vous travaillez - dans ce cas ExampleModel
. Mais si vous travaillez avec différents types de modèles, définissez le paramètre type sur 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);
}
}
Après cela, nous avons terminé! Cependant une dernière chose à mentionner: le SortedListAdapter
n'a pas le même add()
, remove()
ni les replaceAll()
méthodes que notre original ExampleAdapter
avait. Il utilise un Editor
objet séparé pour modifier les éléments de la liste auxquels il est possible d'accéder via la edit()
méthode. Donc, si vous souhaitez supprimer ou ajouter des éléments que vous devez appeler, edit()
ajoutez et supprimez les éléments sur cette Editor
instance et une fois que vous avez terminé, appelez- commit()
le pour appliquer les modifications à SortedList
:
mAdapter.edit()
.remove(modelToRemove)
.add(listOfModelsToAdd)
.commit();
Toutes les modifications que vous apportez de cette façon sont regroupées pour augmenter les performances. La replaceAll()
méthode que nous avons implémentée dans les chapitres ci-dessus est également présente sur cet Editor
objet:
mAdapter.edit()
.replaceAll(mModels)
.commit();
Si vous oubliez d'appeler, commit()
aucune de vos modifications ne sera appliquée!
Tout ce que vous devez faire est d'ajouter une
filter
méthode dansRecyclerView.Adapter
:itemsCopy
est initialisé dans le constructeur de l'adaptateur commeitemsCopy.addAll(items)
.Si vous le faites, appelez simplement
filter
deOnQueryTextListener
:C'est un exemple de filtrage de mon répertoire par nom et numéro de téléphone.
la source
En suivant @Shruthi Kamoji d'une manière plus propre, nous pouvons simplement utiliser un filtrable, son destiné à cela:
Le E ici est un type générique, vous pouvez l'étendre en utilisant votre classe:
Ou changez simplement l'E pour le type que vous voulez (
<CustomerModel>
par exemple)Puis à partir de searchView (le widget que vous pouvez mettre sur menu.xml):
la source
où
la source
Dans l'adaptateur:
En activité:
la source
Avec les composants d'architecture Android grâce à l'utilisation de LiveData, cela peut être facilement implémenté avec tout type d' adaptateur . Vous devez simplement effectuer les étapes suivantes:
1. Configurez vos données pour revenir de la base de données de salle en tant que LiveData comme dans l'exemple ci-dessous:
2. Créez un objet ViewModel pour mettre à jour vos données en direct grâce à une méthode qui connectera votre DAO et votre interface utilisateur
3. Appelez vos données à partir du ViewModel à la volée en passant la requête via onQueryTextListener comme ci-dessous:
À l'intérieur,
onCreateOptionsMenu
définissez votre auditeur comme suitConfigurez votre écouteur de requête quelque part dans votre classe SearchActivity comme suit
Remarque : les étapes (1.) et (2.) sont une implémentation AAC ViewModel et DAO standard , la seule véritable "magie" en cours ici est dans OnQueryTextListener qui mettra à jour les résultats de votre liste dynamiquement à mesure que le texte de la requête change.
Si vous avez besoin de plus de précisions à ce sujet, n'hésitez pas à demander. J'espère que cela a aidé :).
la source
C'est mon point de vue sur l'expansion de la réponse @klimat pour ne pas perdre l'animation de filtrage.
Fondamentalement, il recherche une liste complète et ajoute / supprime des éléments à une liste filtrée un par un.
la source
Je recommande de modifier la solution de @Xaver Kapeller avec 2 choses ci-dessous pour éviter un problème après avoir effacé le texte recherché (le filtre ne fonctionnait plus) car la liste de l'adaptateur a une taille plus petite que la liste des filtres et l'exception IndexOutOfBoundsException s'est produite. Le code doit donc être modifié comme ci-dessous
Et modifiez également dans la fonctionnalité moveItem
J'espère que cela pourrait vous aider!
la source
Adapter
la mauvaise façon. Sans voir votre code, je suppose que le problème le plus probable est que vous ne passez pas une copie de la liste avec tous les éléments auAdapter
.Ajoutez une interface dans votre adaptateur.
implémentez l'interface dans votre activité principale et remplacez la méthode. @Override public void selectedUser (UserModel userModel) {
Tutoriel complet et code source: Recyclerview avec searchview et onclicklistener
la source
J'ai résolu le même problème en utilisant le lien avec quelques modifications. Filtre de recherche sur RecyclerView avec cartes. Est-ce même possible? (J'espère que cela t'aides).
Voici ma classe d'adaptateur
// Classe de filtre
}
// Classe d'activité
Dans la méthode OnQueryTextChangeListener (), utilisez votre adaptateur. Je l'ai moulé en fragments car mon adpter est en fragments. Vous pouvez utiliser l'adaptateur directement s'il est dans votre classe d'activité.
la source