Как фильтровать RecyclerView с помощью SearchView

Я пытаюсь реализовать SearchView из библиотеки поддержки. Я хочу, чтобы пользователь использовал SearchView для фильтрации 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); } } } 

Все, что вам нужно сделать, это добавить 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; } }); 

Это пример фильтрации моей телефонной книги по имени и номеру телефона.

Следуя @Shruthi 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; GenericRecycleAdapter.this.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 (виджет можно поместить в menu.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; } }); 

Я рекомендую изменить решение @Xaver Kapeller на 2 вещи ниже, чтобы избежать проблемы после того, как вы очистили искомый текст (фильтр больше не работал) из-за того, что список адаптеров имеет меньший размер, чем список фильтров, и произошло событие 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); } } 

Надеюсь, что это может вам помочь!

I followed these steps and it worked well, not so complicated and it works pretty good we are surfing around same sittuation with different ways Hope it helps

simply create two list in adapter one orignal and one temp and implements 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; } 

I have solved the same problem using the link with some modifications in it. Search filter on RecyclerView with Cards. Возможно ли это? (hope this helps).

Here is my adapter class

 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); } } 

//Filter class

 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(); } 

}

//Activity class

 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; } } 

In OnQueryTextChangeListener() method use your adapter. I have casted it to fragment as my adpter is in fragment. You can use the adapter directly if its in your activity class.