One of the most requested enhancements for the Random Contact App is for a way to filter contacts with the custom contact picker that is used in it. Since it seemed like a good reusable component, I added the module named MultiContactPicker along with the rearchitecture of my Andorid library project lib-aeApps. Lets see how to add a filter for a RecyclerView in an Android app. As you should have guessed, when we apply a filter we would show a subset of the data in the list. From the RecyclerView’s point of view, the list of data that it has to show has changed - a call to notifyDataSetChanged() is to be expected. And where do we manage the data for a RecyclerView? The backing adapter which extends the class RecyclerView.Adapter. The first step is for the adapter to implement the android.widget.Filterable class class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> implements Filterable { ... } Next, we create a copy of the source data. But use the filtered list to render the view. private final List<ContactInfo> contactInfoList; private List<ContactInfo> filteredContacts; MyRecyclerViewAdapter(final List<ContactInfo> values) { filteredContacts = new ArrayList<>(values); contactInfoList = values; } When we implementing the Filterable class, we need to add a method called getFilter(). This is where the actual filtering occurs. The performFiltering() method is passed in a search string, and we need to filter the list data based on this. The filtering criteria is upto your specific use case. I have added some optimizations so that our friend notifyDataSetChanged() is only called when required. And ofcourse when the search string is empty, we copy back the original list. @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence charSequence) { boolean dataUpdated = false; if (charSequence.length() == 0) { if (filteredContacts.size() < contactInfoList.size()) { filteredContacts = new ArrayList<>(contactInfoList); dataUpdated = true; } } else { filteredContacts = filterContactsByName(charSequence.toString().toLowerCase()); dataUpdated = true; } FilterResults filterResults = new FilterResults(); filterResults.values = dataUpdated; return filterResults; } @Override protected void publishResults(CharSequence charSequence, FilterResults filterResults) { boolean dataUpdated = Boolean.parseBoolean(filterResults.values.toString()); if (dataUpdated) { notifyDataSetChanged(); } } }; } Next step is to add a SearchView to the layout which includes the RecyclerView. <androidx.appcompat.widget.SearchView android:id="@+id/multiContactSearchView" android:layout_width="match_parent" android:layout_height="wrap_content" app:iconifiedByDefault="false" app:queryHint="@string/str_multi_contact_search_hint" /> And finally in the parent activity or Fragment, we respond to the QueryText of the SearchView. Whenever there is a change in text, we simply invoke the filter() method on the adapter. searchView = findViewById(R.id.multiContactSearchView); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { adapter.getFilter().filter(newText); return false; } }); These are all the changes that are required. Pretty much all of the above code except the implementation for filterContactsByName() is generic and can be re-used for any project. Please reach out to me for any doubts. See the references to the source code hosted on GitHub. References Random Contact App lib-aeapps MultiContactRecyclerViewAdapter Sample App