How to filter a RecyclerView with a SearchView

后端 未结 11 1721
孤独总比滥情好
孤独总比滥情好 2020-11-22 00:28

I am trying to implement the SearchView from the support library. I want the user to be to use the SearchView to filter a List of movi

相关标签:
11条回答
  • 2020-11-22 00:31

    Following @Shruthi Kamoji in a cleaner way, we can just use a filterable, its meant for that:

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

    The E here is a Generic Type, you can extend it using your class:

    public class customerAdapter extends GenericRecycleAdapter<CustomerModel>
    

    Or just change the E to the type you want (<CustomerModel> for example)

    Then from searchView (the widget you can put on 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;
        }
    });
    
    0 讨论(0)
  • 2020-11-22 00:31

    With Android Architecture Components through the use of LiveData this can be easily implemented with any type of Adapter. You simply have to do the following steps:

    1. Setup your data to return from the Room Database as LiveData as in the example below:

    @Dao
    public interface CustomDAO{
    
    @Query("SELECT * FROM words_table WHERE column LIKE :searchquery")
        public LiveData<List<Word>> searchFor(String searchquery);
    }
    

    2. Create a ViewModel object to update your data live through a method that will connect your DAO and your UI

    public class CustomViewModel extends AndroidViewModel {
    
        private final AppDatabase mAppDatabase;
    
        public WordListViewModel(@NonNull Application application) {
            super(application);
            this.mAppDatabase = AppDatabase.getInstance(application.getApplicationContext());
        }
    
        public LiveData<List<Word>> searchQuery(String query) {
            return mAppDatabase.mWordDAO().searchFor(query);
        }
    
    }
    

    3. Call your data from the ViewModel on the fly by passing in the query through onQueryTextListener as below:

    Inside onCreateOptionsMenu set your listener as follows

    searchView.setOnQueryTextListener(onQueryTextListener);
    

    Setup your query listener somewhere in your SearchActivity class as follows

    private android.support.v7.widget.SearchView.OnQueryTextListener onQueryTextListener =
                new android.support.v7.widget.SearchView.OnQueryTextListener() {
                    @Override
                    public boolean onQueryTextSubmit(String query) {
                        getResults(query);
                        return true;
                    }
    
                    @Override
                    public boolean onQueryTextChange(String newText) {
                        getResults(newText);
                        return true;
                    }
    
                    private void getResults(String newText) {
                        String queryText = "%" + newText + "%";
                        mCustomViewModel.searchQuery(queryText).observe(
                                SearchResultsActivity.this, new Observer<List<Word>>() {
                                    @Override
                                    public void onChanged(@Nullable List<Word> words) {
                                        if (words == null) return;
                                        searchAdapter.submitList(words);
                                    }
                                });
                    }
                };
    

    Note: Steps (1.) and (2.) are standard AAC ViewModel and DAO implementation, the only real "magic" going on here is in the OnQueryTextListener which will update the results of your list dynamically as the query text changes.

    If you need more clarification on the matter please don't hesitate to ask. I hope this helped :).

    0 讨论(0)
  • 2020-11-22 00:31

    I recommend modify the solution of @Xaver Kapeller with 2 things below to avoid a problem after you cleared the searched text (the filter didn't work anymore) due to the list back of adapter has smaller size than filter list and the IndexOutOfBoundsException happened. So the code need to modify as below

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

    And modify also in moveItem functionality

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

    Hope that It could help you!

    0 讨论(0)
  • 2020-11-22 00:33

    I have solved the same problem using the link with some modifications in it. Search filter on RecyclerView with Cards. Is it even possible? (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.

    0 讨论(0)
  • 2020-11-22 00:39

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

    where

    public GenericBaseAdapter(Context mContext, List<T> itemList) {
            this.mContext = mContext;
            this.itemList = itemList;
            this.origList = itemList;
        }
    
    0 讨论(0)
  • 2020-11-22 00:46

    All you need to do is to add filter method in 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 is initialized in adapter's constructor like itemsCopy.addAll(items).

    If you do so, just call filter from 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;
        }
    });
    

    It's an example from filtering my phonebook by name and phone number.

    0 讨论(0)
提交回复
热议问题