Вступ
Оскільки з вашого питання не зовсім зрозуміло, з чим саме у вас виникають проблеми, я написав цей короткий посібник про те, як реалізувати цю функцію; якщо у вас все ще є запитання, не соромтеся задавати питання.
У мене є робочий приклад того, про що я говорю тут, у цьому сховищі GitHub .
Якщо ви хочете дізнатися більше про приклад проекту, відвідайте домашню сторінку проекту .
У будь-якому випадку результат повинен виглядати приблизно так:

Якщо ви спершу хочете пограти з демонстраційним додатком, ви можете встановити його з Play Store:

Все одно давайте почнемо.
Налаштування SearchView
У папці res/menuстворіть новий файл під назвою main_menu.xml. У неї додати елемент і встановити actionViewClassв android.support.v7.widget.SearchView. Оскільки ви використовуєте бібліотеку підтримки, вам потрібно використовувати простір імен бібліотеки підтримки, щоб встановити actionViewClassатрибут. Ваш XML-файл повинен виглядати приблизно так:
<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>
У своєму Fragmentабо Activityвам доведеться надути це меню xml як звичайне, тоді ви можете шукати, MenuItemщо містить SearchViewі реалізувати те, OnQueryTextListenerщо ми збираємось використати для прослуховування змін у тексті, введеному в 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;
}
І тепер SearchViewготовий до використання. Ми будемо реалізовувати логіку фільтра згодом, onQueryTextChange()як тільки ми закінчимо реалізацію Adapter.
Налаштування Adapter
Перш за все це клас моделей, який я збираюся використовувати для цього прикладу:
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;
}
}
Це просто ваша основна модель, яка відобразить текст у полі RecyclerView. Це макет, який я буду використовувати для відображення тексту:
<?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>
Як ви бачите, я використовую прив'язку даних. Якщо ви ніколи не працювали з прив'язкою даних раніше, не перешкоджайте! Це дуже просто і потужно, проте я не можу пояснити, як це працює в рамках цієї відповіді.
Це ViewHolderдля ExampleModelкласу:
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);
}
}
Знову нічого особливого. Він просто використовує прив'язку даних для прив'язки класу моделі до цього макета, як ми визначили в макеті xml вище.
Тепер ми можемо нарешті підійти до дійсно цікавої частини: Написання адаптера. Я збираюся пропустити основну реалізацію Adapterі замість цього зосереджуся на частинах, які мають значення для цієї відповіді.
Але спочатку є одне, про що ми повинні поговорити: SortedListКлас.
SortedList
Це SortedListабсолютно дивовижний інструмент, який є частиною RecyclerViewбібліотеки. Він піклується про сповіщення Adapterпро зміни в наборі даних і робить це дуже ефективним способом. Єдине, що вам потрібно зробити - це вказати порядок елементів. Вам потрібно зробити це, застосувавши compare()метод, який порівнює два елементи SortedListпросто так, як Comparator. Але замість сортування a Listвикористовується для сортування елементів у RecyclerView!
У SortedListвзаємодіє з Adapterчерез Callbackклас , який ви повинні реалізувати:
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();
}
}
У способах у верхній частині зворотного виклику , як onMoved, onInsertedі т.д. Ви повинні викликати еквівалент повідомить вами спосіб Adapter. Три способи внизу compare, areContentsTheSameі areItemsTheSameви повинні реалізувати відповідно до того, який тип об’єктів ви хочете відображати та в якому порядку ці об'єкти повинні з’являтися на екрані.
Давайте переглянемо ці методи по черзі:
@Override
public int compare(ExampleModel a, ExampleModel b) {
return mComparator.compare(a, b);
}
Це compare()метод, про який я говорив раніше. У цьому прикладі я просто передаю дзвінок на те, Comparatorщо порівнює дві моделі. Якщо ви хочете, щоб елементи відображалися в алфавітному порядку на екрані. Цей порівняльник може виглядати приблизно так:
private static final Comparator<ExampleModel> ALPHABETICAL_COMPARATOR = new Comparator<ExampleModel>() {
@Override
public int compare(ExampleModel a, ExampleModel b) {
return a.getText().compareTo(b.getText());
}
};
Тепер розглянемо наступний метод:
@Override
public boolean areContentsTheSame(ExampleModel oldItem, ExampleModel newItem) {
return oldItem.equals(newItem);
}
Мета цього методу - визначити, чи змінився вміст моделі. В SortedListвикористовує це , щоб визначити , чи потребує викликатися подія зміни - іншими словами , якщо RecyclerViewнеобхідно кроссфейд стару і нову версію. Якщо у модельних класах є правильна equals()та hashCode()реалізація, зазвичай ви можете просто реалізувати її, як вище. Якщо ми додамо equals()та додаток hashCode()до ExampleModelкласу, він повинен виглядати приблизно так:
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;
}
}
Швидка зауваження: у більшості IDE, таких як Android Studio, IntelliJ та Eclipse, є функціонал для створення equals()та hashCode()реалізації для вас одним натисненням кнопки! Тож вам не доведеться їх реалізовувати самостійно. Подивіться в Інтернеті, як це працює у вашому IDE!
Тепер давайте розглянемо останній метод:
@Override
public boolean areItemsTheSame(ExampleModel item1, ExampleModel item2) {
return item1.getId() == item2.getId();
}
Цей SortedListметод використовує для перевірки, чи два елементи відносяться до однієї речі. Найпростіше кажучи (не пояснюючи, як це SortedListпрацює), це використовується для визначення того, чи об’єкт уже міститься в програмі, Listі чи потрібно відтворювати анімацію додавання, переміщення або зміни. Якщо у ваших моделей є ідентифікатор, ви зазвичай порівнюєте лише ідентифікатор у цьому методі. Якщо вони не потребують іншого способу, щоб перевірити це, але ви все-таки реалізуєте це, залежить від конкретного додатка. Зазвичай це найпростіший варіант, щоб надати всім моделям ідентифікатор - який, наприклад, може бути полем первинного ключа, якщо ви запитуєте дані з бази даних.
При SortedList.Callbackправильному виконанні ми можемо створити екземпляр SortedList:
final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);
В якості першого параметра в конструкторі SortedListвам потрібно передати клас ваших моделей. Інший параметр - це лише SortedList.Callbackвизначений нами вище.
Тепер приступимо до справи: Якщо ми реалізуємо Adapterз a, SortedListце має виглядати приблизно так:
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();
}
}
ComparatorВикористовується для сортування пункт передається через конструктор , тому ми можемо використовувати один і той же , Adapterнавіть якщо елементи повинні відображатися в іншому порядку.
Зараз ми майже готові! Але для початку нам потрібен спосіб додавання або видалення елементів до Adapter. З цією метою ми можемо додати методи, до Adapterяких можна додавати та видаляти елементи до 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();
}
Нам тут не потрібно викликати будь-які способи сповіщення, оскільки це SortedListвже робиться через SortedList.Callback! Крім цього, реалізація цих методів є досить прямою вперед за одним винятком: метод видалення, який видаляє Listмодель. Оскільки SortedListіснує лише один метод видалення, який може видалити один об'єкт, нам потрібно перевести список і видалити моделі по черзі. Виклик beginBatchedUpdates()на початку містить усі зміни, які ми збираємось внести SortedListразом, і покращує продуктивність. Коли ми називаємо повідомляється про всі зміни відразу.endBatchedUpdates()RecyclerView
Крім того, ви повинні розуміти, що якщо ви додасте об'єкт до SortedListі він вже є, SortedListвін більше не буде доданий. Натомість SortedListвикористовує areContentsTheSame()метод, щоб з'ясувати, чи змінився об'єкт - і чи є у нього елемент у RecyclerViewзаповіті буде оновлено.
У будь-якому випадку, як правило, я вважаю за краще один метод, який дозволяє мені замінити всі елементи в RecyclerViewодразу. Видаліть усе, чого немає, Listі додайте всі елементи, які відсутні у 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();
}
Цей метод знову збирає всі оновлення разом для підвищення продуктивності. Перший цикл знаходиться в зворотному порядку, оскільки вилучення елемента на початку може зіпсувати індекси всіх елементів, які з’являються після нього, і це може призвести в деяких випадках до проблем, таких як невідповідності даних. Після цього ми просто додаємо Listдо SortedListвикористовуваного, addAll()щоб додати всі елементи, які вже не перебувають у - SortedListі, як я описав вище, - оновити всі елементи, які вже є, SortedListале змінилися.
І цим Adapterє повне. Вся справа повинна виглядати приблизно так:
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();
}
}
Єдине, чого зараз бракує, це здійснити фільтрацію!
Реалізація логіки фільтра
Для реалізації логіки фільтру спершу треба визначити a Listз усіх можливих моделей. Для цього прикладу я створюю Listз ExampleModelпримірників з масиву фільмів:
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);
}
Нічого особливого не відбувається тут, ми просто створимо його Adapterі встановимо його RecyclerView. Після цього ми створюємо Listмоделі з імен фільмів у MOVIESмасиві. Потім ми додаємо всі моделі до SortedList.
Тепер ми можемо повернутися до того, onQueryTextChange()що ми визначили раніше, і розпочати реалізацію логіки фільтра:
@Override
public boolean onQueryTextChange(String query) {
final List<ExampleModel> filteredModelList = filter(mModels, query);
mAdapter.replaceAll(filteredModelList);
mBinding.recyclerView.scrollToPosition(0);
return true;
}
Це знову досить прямо вперед. Ми називаємо метод filter()і передати в Listз ExampleModelх, а також в рядку запиту. Потім ми викликаємо replaceAll()на Adapterі передати в відфільтрованої Listповертається filter(). Ми також повинні викликати scrollToPosition(0)на RecyclerViewдля того, щоб користувач завжди може побачити всі деталі при пошуку чогось - то. Інакше RecyclerViewпід час фільтрування ви можете залишитися в положенні прокрутки вниз і згодом приховати кілька елементів. Прокручування до верху забезпечує кращу роботу користувачів під час пошуку.
Єдине, що залишилося зараз зробити - це реалізувати 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;
}
Перше, що ми робимо тут - це дзвінок toLowerCase()у рядку запиту. Ми не хочемо, щоб наша пошукова функція була чутливою до регістру, і, зателефонувавши toLowerCase()до всіх порівняних рядків, ми можемо забезпечити повернення однакових результатів незалежно від випадку. Потім він просто повторює всі моделі в Listми передали в нього і перевіряє, чи рядок запиту міститься в тексті моделі. Якщо це так, модель додається до відфільтрованого List.
І це все! Наведений вище код працюватиме на рівні 7 і вище API, і, починаючи з рівня 11 API, ви отримуєте анімацію предмета безкоштовно!
Я розумію, що це дуже детальний опис, який, ймовірно, робить всю цю справу більш складною, ніж є насправді, але є спосіб, як ми можемо узагальнити всю цю проблему та зробити реалізацію на Adapterоснові SortedListнабагато простішою.
Узагальнення проблеми та спрощення адаптера
У цьому розділі я не збираюся детально описуватись - частково тому, що я переживаю обмеження символів для відповідей на переповнення стека, а також тому, що більшість із них уже пояснено вище - але підсумовую зміни: Ми можемо реалізувати базовий Adapterклас яка вже піклується про поводження з SortedListмоделями, а також прив'язування до ViewHolderпримірників і забезпечує зручний спосіб реалізації Adapterоснови на основі SortedList. Для цього ми повинні зробити дві речі:
- Нам потрібно створити
ViewModelінтерфейс, який мають реалізувати всі класи моделей
- Нам потрібно створити
ViewHolderпідклас, який визначає bind()метод, який Adapterможна використовувати для автоматичного прив’язування моделей.
Це дозволяє нам просто зосередитись на вмісті, який повинен бути відображений у RecyclerViewпростому впровадженні моделей, а також відповідних ViewHolderреалізаціях. Використовуючи цей базовий клас, нам не потрібно турбуватися про складні деталі Adapterта його SortedList.
SortedListAdapter
Через обмеження символів для відповідей на StackOverflow я не можу пройти кожен етап реалізації цього базового класу або навіть додати тут повний вихідний код, але ви можете знайти повний вихідний код цього базового класу - я його назвав SortedListAdapter- у цьому GitHub Gist .
Щоб зробити ваше життя простим, я опублікував бібліотеку на jCenter, яка містить SortedListAdapter! Якщо ви хочете використовувати його, то все, що вам потрібно зробити, це додати цю залежність до файлу build.gradle програми:
compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'
Ви можете знайти більше інформації про цю бібліотеку на домашній сторінці бібліотеки .
Використання SortedListAdapter
Для використання SortedListAdapterми повинні внести дві зміни:
Змініть ViewHolderтак, щоб він розширився SortedListAdapter.ViewHolder. Параметр типу повинен бути моделлю, яка повинна бути пов'язана з цим ViewHolder- в цьому випадку ExampleModel. Ви повинні прив’язувати дані до своїх моделей performBind()замість 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);
}
}
Переконайтесь, що всі ваші моделі реалізують ViewModelінтерфейс:
public class ExampleModel implements SortedListAdapter.ViewModel {
...
}
Після цього нам просто потрібно оновити ExampleAdapterрозширення SortedListAdapterта видалити все, що більше не потрібно. Параметр типу повинен бути типом моделі, з якою ви працюєте - в цьому випадку ExampleModel. Але якщо ви працюєте з моделями різних типів, тоді встановіть параметр типу 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);
}
}
Після цього ми робимо! Однак одне останнє, що слід зазначити: У них SortedListAdapterнемає тих же методів add(), remove()чи у replaceAll()наших оригіналів ExampleAdapter. Він використовує окремий Editorоб'єкт для зміни елементів у списку, до яких можна отримати доступ edit()методом. Отже, якщо ви хочете видалити або додати елементи, вам потрібно зателефонувати, edit()потім додайте та видаліть елементи цього Editorпримірника, і як тільки ви це зробите, зателефонуйте commit()йому, щоб застосувати зміни до SortedList:
mAdapter.edit()
.remove(modelToRemove)
.add(listOfModelsToAdd)
.commit();
Усі внесені вами зміни збираються разом для підвищення продуктивності. replaceAll()Метод ми реалізували в попередніх розділах також присутній на цьому Editorоб'єкті:
mAdapter.edit()
.replaceAll(mModels)
.commit();
Якщо ви забудете зателефонувати, commit()то жодна з ваших змін не буде застосована!