I found solutions for filters on ListView
and SearchView
on RecyclerView
separately, but I wish to combine them. Is it even possible?
Asked
Active
Viewed 3.8k times
21
3 Answers
82
Yes it is possible Your RecyclerView.Adapter
can implement Filterable
. After that you have to override Filter getFilter()
method.
You have to define your own filter as is shown in the code below:
@Override
public Filter getFilter() {
return new YourFilterClass();
}
YourFilterClass
class YourFilterClass extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
//Here you have to implement filtering way
final FilterResults results = new FilterResults();
//logic to filtering
results.values = ...
results.count = ...
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
// here you can use result - (f.e. set in in adapter list)
}
}
Example
public class UserListAdapter extends RecyclerView.Adapter<UserListAdapter.ViewHolder> implements Filterable {
private final List<User> userList;
private final List<User> filteredUserList;
private UserFilter userFilter;
public UserListAdapter(Context context) {
this.userList =new ArrayList<>();
this.filteredUserList = new ArrayList<>();
}
///... other methods
@Override
public Filter getFilter() {
if(userFilter == null)
userFilter = new UserFilter(this, userList);
return userFilter;
}
private static class UserFilter extends Filter {
private final UserListAdapter adapter;
private final List<User> originalList;
private final List<User> filteredList;
private UserFilter(UserListAdapter adapter, List<User> originalList) {
super();
this.adapter = adapter;
this.originalList = new LinkedList<>(originalList);
this.filteredList = new ArrayList<>();
}
@Override
protected FilterResults performFiltering(CharSequence constraint) {
filteredList.clear();
final FilterResults results = new FilterResults();
if (constraint.length() == 0) {
filteredList.addAll(originalList);
} else {
final String filterPattern = constraint.toString().toLowerCase().trim();
for (final User user : originalList) {
if (user.getName().contains(filterPattern)) {
filteredList.add(user);
}
}
}
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
adapter.filteredUserList.clear();
adapter.filteredUserList.addAll((ArrayList<User>) results.values);
adapter.notifyDataSetChanged();
}
}
}
After that in the place when you want to filtering call:
userListAdapter.getFilter().filter(text)

Konrad Krakowiak
- 12,285
- 11
- 58
- 45
-
RecyclerView doesn't support filtering out-of-the-box, imho – pskink Apr 22 '15 at 08:57
-
@pskink As far I know no . Adapter from `RecyclerView` is a just `public static abstract class Adapter
{` Maybe there is some mechanism but I don't know it. My solution works :) – Konrad Krakowiak Apr 22 '15 at 09:01 -
who calls getFilter() then? i don't see anything like that in RecyclerView code... – pskink Apr 22 '15 at 09:08
-
so your adapter doesn't need to implements Filterable, just use the custom Filter, thats it – pskink Apr 22 '15 at 09:16
-
you have to implements Filterable because you need getFilter method. – Konrad Krakowiak Apr 22 '15 at 09:18
-
Look on this line: `userListAdapter.getFilter().filter(text)` you call getFilter() on adapter – Konrad Krakowiak Apr 22 '15 at 09:19
-
you can also use userListAdapter.getMyFancyFilter(), right? so no Filterable is required – pskink Apr 22 '15 at 09:23
-
Yes that's right for this case :) but I you want use some mechanism which uses Filterable - you have to implement it. So better is use Filterable :) – Konrad Krakowiak Apr 22 '15 at 09:24
-
Yeah it is helpful. But the implementation is getting dirty as I'm new to whole search and filter concept. Anyways I'll accept it. Thank you for the help. – priyank Apr 22 '15 at 09:28
-
I am glad that I could help you – Konrad Krakowiak Apr 22 '15 at 09:29
-
no, Filterable was needed by AbsListView for it to test if the Adapter support filtering, it was done by testing "instanceof Filterable", your adapter support filtering so Filterable is not needed, you dont need to test if it is an instance of Filterable – pskink Apr 22 '15 at 09:29
-
Good to know :) But in my project I used Filterable for my custom mechanism, I can create my own interface but Filterable was enough for me :) – Konrad Krakowiak Apr 22 '15 at 09:32
-
@KonradKrakowiak I've implemented the whole search logic now how do I get the search bar, with SearchView? Or is it different in case of filter? – priyank Apr 22 '15 at 10:36
-
1search.setOnQueryTextListener(new OnQueryTextListenerAdapter() { @Override public boolean onQueryTextSubmit(String filterString) { yourAdapter.getFilter().filter(filterString); return true; } }); – Konrad Krakowiak Apr 22 '15 at 11:30
-
It's a nice example but to be able to handle realtime on character change search you need to maintain the original list because for now it does keep only a filtered list – Nasz Njoka Sr. Nov 06 '15 at 10:38
-
Works great. Thank you! – luizMello Jan 06 '17 at 12:25
-
Works great too. Thank you! – Morton Jan 19 '17 at 01:58
-
You could simplify your code a bit if your user UserFilter class wasnt static – yarrichar May 30 '17 at 22:47
-
Please add this ans with ...... if (user.getName()..toLowerCase().trim().contains(filterPattern)) ...... so it wih match your search otherwise there will be not match with contains – GovindRathod Apr 04 '21 at 09:07
3
Here is complete sample code
Model Class
public class Skills {
int id;
String skill;
boolean isSelected;
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
Adapter Class
private static final String TAG = SkillAdapter.class.getSimpleName();
protected List<Skills> mOriginalData = new ArrayList<>();
protected List<Skills> mResultData = new ArrayList<>();
protected Activity mActivity;
OnRecyclerViewClick onRecyclerViewClick;
private ItemFilter mFilter = new ItemFilter();
public SkillAdapter(Activity activity, OnRecyclerViewClick onRecyclerViewClick) {
mActivity = activity;
this.onRecyclerViewClick = onRecyclerViewClick;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_skill,
parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Skills data = mResultData.get(position);
try {
final ViewHolder viewHolder = (ViewHolder) holder;
if (data != null) {
viewHolder.checkSkill.setText(data.getSkill());
viewHolder.checkSkill.setChecked(data.isSelected());
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getItemCount() {
return mResultData.size();
}
public void addItem(Skills exam) {
mOriginalData.add(exam);
mResultData.add(exam);
int index = mOriginalData.indexOf(exam);
notifyItemInserted(index);
}
public void removeItem(int index) {
mOriginalData.remove(index);
notifyItemRemoved(index);
}
public void removeItem(Skills exam) {
int index = mOriginalData.indexOf(exam);
mOriginalData.remove(exam);
notifyItemRemoved(index);
}
public Filter getFilter() {
return mFilter;
}
public Skills getItem(int index) {
return mOriginalData.get(index);
}
public void replaceItem(int index, Skills audioMeta) {
mOriginalData.set(index, audioMeta);
notifyItemChanged(index);
}
public class ViewHolder extends RecyclerView.ViewHolder {
CheckBox checkSkill;
public ViewHolder(View itemView) {
super(itemView);
checkSkill = (CheckBox) itemView.findViewById(R.id.chkSkill);
checkSkill.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onRecyclerViewClick.onItemClick(v, getLayoutPosition());
}
});
}
}
private class ItemFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
String filterString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
int count = mOriginalData.size();
final ArrayList<Skills> tempFilterList = new ArrayList<Skills>(count);
String filterableString;
for (int i = 0; i < count; i++) {
filterableString = mOriginalData.get(i).getSkill();
if (filterableString.toLowerCase().contains(filterString)) {
tempFilterList.add(mOriginalData.get(i));
}
}
results.values = tempFilterList;
results.count = tempFilterList.size();
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mResultData.clear();
mResultData = (ArrayList<Skills>) results.values;
notifyDataSetChanged();
}
}
In Activity Use
mAdapter = new SkillAdapter(SkillsActivity.this, SkillsActivity.this);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
recyclerView.setAdapter(mAdapter);
Then Filter
editSearch.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mAdapter.getFilter().filter(editSearch.getText().toString());
}
});

user2837615
- 296
- 1
- 5
- 12
-
Thanks, your code is working, but how to load all data by default? – Rakesh Saini Oct 02 '22 at 17:55
2
Inside Fragment Class
declare that:
protected List<User> mDataset;
protected List<User> mDataOrigin;
then inside onCreate
add same source destination to both dataSet ant dataOrigin
mDataset = getObjectsFromDB();
mDataOrigin = getObjectsFromDB();
Finally use the magic function:
private void filterRecyclerView(String charText){
charText = charText.toLowerCase();
clearDataSet();
if (charText.length() == 0) {
mDataset.addAll(mDataOrigin);
} else {
for (User user : mDataOrigin) {
if (user.getName().toLowerCase().contains(charText)) {
mDataset.add(user);
}
}
}
mAdapter.notifyDataSetChanged();
}
Notice User
is the list content you can replace with your Object have fun :)

Gustavo Morales
- 2,614
- 9
- 29
- 37

Mostafa Anter
- 3,445
- 24
- 26
-
future readers make sure you don't forget `mDataset = getObjectsFromDB(); mDataOrigin = getObjectsFromDB();` to set from the source itself or clone the mDataset, as i spent some hours looking into the issue by assigning `mDataOrigin = mDataset`, P.S. `mDataOrigin = mDataset.clone()` worked fine for me if you don't wanna hit db/func call – Anup Jan 23 '17 at 19:55