Как фильтровать RecyclerView с помощью SearchView
Я пытаюсь реализовать SearchView из библиотеки поддержки. Я хочу, чтобы пользователь использовал SearchView для фильтрации a List из фильма RecyclerView.
я последовал за несколько учебников до сих пор и я добавил SearchView до ActionBar, но я не совсем уверен, куда идти отсюда. Я видел несколько примеров, но ни один из них не показывает результатов, когда вы начинаете печатать.
это мой 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);
}
}
и это мой 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);
}
}
}
7 ответов:
введение
поскольку не совсем ясно, с чем именно у вас возникли проблемы, я написал это краткое пошаговое руководство о том, как реализовать эту функцию, если у вас все еще есть вопросы, не стесняйтесь спрашивать.
у меня есть рабочий пример всего, о чем я говорю здесь в этом Репозиторий GitHub.
Если вы хотите узнать больше о пример проекта посетите Домашняя страница проекта.в любом случае результат должен выглядеть примерно так:
если вы сначала хотите поиграть с демо-приложением, вы можете установить его из Play Store:
в любом случае, давайте начнем.
настройка
SearchViewв папке
res/menuсоздать новый файл называетсяmain_menu.xml. В нем добавьте элемент и установитеactionViewClassдоandroid.support.v7.widget.SearchView. Поскольку вы используете библиотеку поддержки, вы должны использовать пространство имен библиотеки поддержки, чтобы установить . Ваш 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) MenuItemCompat.getActionView(searchItem); 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. Это макет I собираюсь использовать для отображения текста:<?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
The
SortedList- это совершенно удивительный инструмент который является частьюRecyclerViewбиблиотека. Он заботится о уведомленииAdapterоб изменениях в наборе данных и делает это очень эффективным способом. Единственное, что вам нужно сделать, это указать порядок элементов. Вам нужно сделать это, реализовавcompare()метод, который сравнивает два элемента вSortedListкакComparator. Но вместо сортировкиListиспользуется для сортировки элементов вRecyclerView!The
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и т. д. вы должны вызвать эквивалентный метод notify вашего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(); }The
SortedListиспользует этот метод, чтобы проверить, если два элемента относятся к одной и той же вещи. В самых простых выражениях (не объясняя, какSortedListработает) это используется, чтобы определить, если объект уже содержится вListи если добавить, перемещение или изменение анимации должны быть воспроизведены. Если ваши модели имеют идентификатор, вы обычно сравниваете только идентификатор в этом методе. Если они этого не делают, вам нужно выяснить какой-то другой способ проверить это, но в конечном итоге вы реализуете это зависит от вашего конкретного приложения. Как правило, это самый простой вариант, чтобы дать всем моделям идентификатор - это может быть, например, поле первичного ключа, если вы запрашиваете данные из базы данных.с
SortedList.Callbackправильно реализовано мы можем создать экземплярSortedList:final SortedList<ExampleModel> list = new SortedList<>(ExampleModel.class, mCallback);как первый параметр в конструкторе
SortedListвам нужно пройти класс ваших моделей. Другой параметр-это простоSortedList.Callbackмы определили выше.теперь давайте перейдем к делу: если мы реализуем
AdapterС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(); } }The
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! Кроме того, реализация этих методов довольно прямо вперед, с одним исключением: метод remove, который удаляетListмоделей. Так какSortedListимеет только один метод удаления, который может удалить один объект, который нам нужно перебрать по списку и удалить модели по одному. ЗвонюbeginBatchedUpdates()в начале партии все изменения, которые мы собираемся сделать наSortedListвместе и повышает производительность. Когда мы зовемendBatchedUpdates()theRecyclerViewуведомления обо всех изменениях сразу.кроме того, что у вас есть нужно понять, что если вы добавляете объект в
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(); } }единственное, чего не хватает сейчас, это реализовать фильтрацию!
реализация логики фильтра
для реализации логики фильтра мы сначала должны определить
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наExampleModels, а также строка запроса. Затем мы вызываемreplaceAll()наAdapterи пройти в фильтрованныйListвозвращеноfilter(). Мы также должны позвонитьscrollToPosition(0)наRecyclerViewчтобы убедиться, что пользователь всегда может видеть все элементы при поиске чего-то. В противном случаеRecyclerViewможет оставаться в прокрученном вниз положении во время фильтрации и впоследствии скрыть несколько элементов. Прокрутка вверх обеспечивает лучший пользовательский интерфейс во время испытующий.единственное, что осталось сделать сейчас, чтобы реализовать :
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.и это все! Приведенный выше код будет работать на уровне API 7 и выше, и начиная с уровня API 11 вы получаете анимацию элементов бесплатно!
я понимаю, что это очень подробное описание, которое, вероятно, делает все это более сложным, чем на самом деле, но есть способ обобщить всю эту проблему и сделать реализацию
Adapterна основеSortedListгораздо проще.
обобщение проблемы и упрощение адаптера
в этом разделе я не буду вдаваться в подробности-отчасти потому, что я сталкиваюсь с ограничением символов для ответов на переполнение стека, но также и потому, что большинство из них уже объяснено выше, но чтобы суммировать изменения: мы можем реализовать базу
Adapterкласс, который уже заботится о решенииSortedListа также привязка моделей кViewHolderэкземпляров и обеспечивает удобный способ реализацииAdapterна основеSortedList. Для этого мы должны сделать две вещи:
- нам нужно создать
ViewModelинтерфейс, который все классы моделей должны реализовать- нам нужно создать
ViewHolderподкласс, который определяет abind()методAdapterможно использовать для автоматической привязки моделей.это позволяет нам просто сосредоточиться на содержании, которое должно отображаться в
RecyclerViewby просто реализуя модели и там соответствующиеViewHolderреализаций. Используя этот базовый класс, нам не нужно беспокоиться о сложных деталяхAdapterиSortedList.SortedListAdapter
из-за ограничения символов для ответов на StackOverflow я не могу пройти каждый шаг реализации этого базового класса или даже добавить полный исходный код здесь, но вы можете найти полный исходный код этого базового класса - я назвал его
SortedListAdapter- in это GitHub Gist.чтобы сделать вашу жизнь простой я опубликовал библиотеку на jCenter, которая содержит
SortedListAdapter! Если вы хотите использовать его, то все, что вам нужно сделать, это добавить эту зависимость для построения вашего приложения.файл gradle:compile 'com.github.wrdlbrnft:sorted-list-adapter:0.2.0.1'вы можете найти дополнительную информацию об этой библиотеке на главной странице библиотеки.
с помощью SortedListAdapter
использовать элемент
SortedListAdapterмы должны сделать два изменения:
изменить
ViewHolderтак, что он простираетсяSortedListAdapter.ViewHolder. Параметр type должен быть моделью, которая должна быть привязана к этому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и удалить все, что нам больше не нужно. Параметр type должен быть типом модели, с которой вы работаете-в этом случае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()метод, который мы реализовали в главах выше также присутствует на этом :mAdapter.edit() .replaceAll(mModels) .commit();если вы забыли назвать
commit()тогда ни одно из ваших изменений не будет применено!
все, что вам нужно сделать, это добавить
filterметод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(); }
itemsCopyинициализируется в конструкторе адаптера, какitemsCopy.addAll(items).если вы это сделаете, просто позвоните
filterС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; } });это пример фильтрации моей телефонной книги по имени и номеру телефона.
после @начала знакомства с Ченнай Kamoji в более чистый путь, мы можем просто использовать фильтрующийся, его имел в виду, что:
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; } }E вот общий тип, вы можете расширить его с помощью вашего класса:
public class customerAdapter extends GenericRecycleAdapter<CustomerModel>или просто измените E на тип, который вы хотите (
<CustomerModel>например)затем из searchView (виджет вы можете поместить в меню.xml):
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; } });
просто создайте два списка в адаптере один orignal и один temp и реализует 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(); } }; }здесь
public GenericBaseAdapter(Context mContext, List<T> itemList) { this.mContext = mContext; this.itemList = itemList; this.origList = itemList; }
Я рекомендую изменить решение @Xaver Kapeller с двумя вещами ниже, чтобы избежать проблемы после того, как вы очистили искомый текст (фильтр больше не работал) из-за того, что список back of adapter имеет меньший размер, чем список фильтров, и произошло исключение IndexOutOfBoundsException. Так что код нужно изменить, как показано ниже
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); } }и изменить также в функциональности moveItem
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); } }надеюсь, что это может помочь вам!
я решил такую же проблему, используя ссылку с некоторыми изменениями в нем. фильтр поиска на RecyclerView с картами. Это вообще возможно? (надеюсь, что это помогает).
вот мой класс адаптера
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); } }//фильтр класс
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(); }}
//активности класс
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; } }в методе OnQueryTextChangeListener () используйте свой адаптер. Я бросил его на фрагмент, как мой адптер находится в фрагменте. Вы можете использовать адаптер непосредственно, если он находится в вашем классе активности.
Блок Питания:
public void setFilter(List<Channel> newList){ mChannels = new ArrayList<>(); mChannels.addAll(newList); notifyDataSetChanged(); }В Работе:
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; } });


Comments