1

I've been trying to add SearchView to my RecyclerView for some time and have referenced these posts here, here and here. At this point, I'm sure the answer is staring me right in the face.

However, I'm not sure how to implement the SearchView when for example, using the following:

private static final List<DataModel> getDummyData(){
    List<DataModel> dummyDataList = new ArrayList<>();

    dummyDataList.add(new DataModel("Alphabet", "Sub Alphabet"));
    dummyDataList.add(new DataModel("Banana", "Sub Banana"));
    dummyDataList.add(new DataModel("Captain", "Sub Captain"));
    dummyDataList.add(new DataModel("Donut", "Sub Donut"));
    dummyDataList.add(new DataModel("Elephant", "Sub Elephant"));
    dummyDataList.add(new DataModel("Fox", "Sub Fox"));
    dummyDataList.add(new DataModel("Giraffe", "Sub Giraffe"));
    dummyDataList.add(new DataModel("Hippo", "Sub Hippo"));
    dummyDataList.add(new DataModel("Iguana", "Sub Iguana"));
    dummyDataList.add(new DataModel("Jumanji", "Sub Jumanji"));

    return dummyDataList;
    }

Here's my current setup (following this tutorial) which uses Locale.getISOCountries(); and searches through the names of countries.

MainActivityFragment:

public class MainActivityFragment extends Fragment implements    SearchView.OnQueryTextListener {

private RecyclerView mRecyclerView;
private List<DataModel> mDataModel;
private RVAdapter adapter;

public MainActivityFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.tab_one_fragment, container, false);

    mRecyclerView = (RecyclerView)view.findViewById(R.id.recyclerview);
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(layoutManager);
    return view;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    setHasOptionsMenu(true);

    String[] locales = Locale.getISOCountries();

    mDataModel = new ArrayList<>();

    for (String countryCode : locales){
        Locale obj = new Locale("", countryCode);
        mDataModel.add(new DataModel(obj.getDisplayCountry(), obj.getISO3Country()));
    }
    adapter = new RVAdapter(mDataModel);
    mRecyclerView.setAdapter(adapter);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.menu_main, menu);

    final MenuItem item = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
    searchView.setOnQueryTextListener(this);

    MenuItemCompat.setOnActionExpandListener(item,
            new MenuItemCompat.OnActionExpandListener() {
                @Override
                public boolean onMenuItemActionExpand(MenuItem item) {
                    return true;
                }
                @Override
                public boolean onMenuItemActionCollapse(MenuItem item) {
                    // Do something when collapsed
                    adapter.setFilter(mDataModel);
                    return true; 
                }
            });
}

@Override
public boolean onQueryTextChange(String newText) {
    final List<DataModel> filteredModeList = filter(mDataModel, newText);
    adapter.setFilter(filteredModeList);
    return true;
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

private List<DataModel>filter(List<DataModel> models, String query){
    query = query.toLowerCase();

    final List<DataModel> filteredModeList = new ArrayList<>();
    for (DataModel model : models){
        final String text = model.getName().toLowerCase();
        if (text.contains(query)){
            filteredModeList.add(model);
        }
    }
    return filteredModeList;
    }
}

And my Adapter-RVAdapter.java:

public class RVAdapter extends RecyclerView.Adapter<ItemViewHolder> {

private List<DataModel> mDataModel;

public RVAdapter(List<DataModel> mDataModel){
    this.mDataModel = mDataModel;
}

@Override
public void onBindViewHolder(ItemViewHolder itemViewHolder, int position) {
    final DataModel model = mDataModel.get(position);
    itemViewHolder.bind(model);
}

@Override
public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int position){
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_row, viewGroup, false);
    return new ItemViewHolder(view);
}

@Override
public int getItemCount() {
    return mDataModel.size();
}

public void setFilter(List<DataModel> dataModels){
    mDataModel = new ArrayList<>();
    mDataModel.addAll(dataModels);
    notifyDataSetChanged();
    }
}

Here's the ItemViewHolder.java:

public class ItemViewHolder extends RecyclerView.ViewHolder {

public TextView name_TextView;
public TextView subName_TextView;

public ItemViewHolder(View itemView){
    super(itemView);

    name_TextView = (TextView)itemView.findViewById(R.id.country_name);
    subName_TextView = (TextView)itemView.findViewById(R.id.country_iso);
}

public void bind(DataModel dataModel){
    name_TextView.setText(dataModel.getName());
    subName_TextView.setText(dataModel.getSubName());
    }
}

And finally, DataModel.java:

public class DataModel {

String name;
String subName;

DataModel(String name, String subName){
    this.name = name;
    this.subName = subName;
}

public String getName(){
    return name;
}

public String getSubName(){
    return subName;
    }
}

Any help, suggestions or a point in the right direction would be awesome!

Community
  • 1
  • 1
ImArtVandalay
  • 316
  • 5
  • 11

3 Answers3

0

This was inspired by the following post:

How to filter a RecyclerView with a SearchView

I needed a little more complex solution because the recyclerview was hosted in a fragment so I used an EventBus to pass the search query from the activity that was hosting the searchview to the fragment.

From the Activity that has access to the SearchView

@Override
public boolean onQueryTextChange(String query) {
    EventBus.getDefault().post(new SearchEvent(query));
    return true;
}

On the Fragment that is hosting the recyclerview:

public void onEvent(SearchEvent e) {
    final List<? extends GenericCompany> filteredModelList = filter(mCompanies, e.getSearchString());
    mListAdapter.replaceData(filteredModelList);
    mRecyclerView.scrollToPosition(0);
}


private List<? extends GenericCompany> filter(List<SCSponsors> models, String query) {
    query = query.toLowerCase();

    final List<SCSponsors> filteredModelList = new ArrayList<>();
    for (SCSponsors model : models) {
        final String text = model.getDisplayName().toLowerCase();
        if (text.contains(query)) {
            filteredModelList.add(model);
        }
    }
    return filteredModelList;
}

Inside the Adapter:

    public void replaceData(List<? extends GenericCompany> companies) {
        setList(companies);
        notifyDataSetChanged();
    }
dazza5000
  • 7,075
  • 9
  • 44
  • 89
0

Alright, so after taking a closer look at the post here, (which this answer is heavily based on and couldn't do without), I ended up with the following below:

Note: I originally tested this out on a project that had a TabLayout, hence some of the tab-related variable and class names. Also, all of my Layouts are the same as those in the post linked above, however using it with TabLayout, CoordinatorLayout/AppBarLayout etc shouldn't be difficult.

Here is the MainFragment.java

public class MainFragment extends Fragment implements SearchView.OnQueryTextListener {

public static MainFragment newInstance() {
    return new MainFragment();
}

RecyclerView mRecyclerView;
private List<MemberData> tabListItem;
private RecyclerViewTabsAdapter mAdapter;


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    final View view = inflater.inflate(R.layout.recyclerview, container, false);
    mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);

    return view;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    setHasOptionsMenu(true);

    mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    mRecyclerView.setHasFixedSize(true);

    tabListItem = new ArrayList<>();

    tabListItem = getTabRowList();

    mAdapter = new RecyclerViewTabsAdapter(getActivity(), tabListItem);
    mRecyclerView.setAdapter(mAdapter);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.menu_main, menu);
    final MenuItem item = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
    searchView.setOnQueryTextListener(this);
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

@Override
public boolean onQueryTextChange(String newText) {
    final List<MemberData> filteredModelList = filter(tabListItem, newText);
    mAdapter.animateTo(filteredModelList);
    mRecyclerView.scrollToPosition(0);

    return true;
}

private List<MemberData> filter(List<MemberData> datas, String newText) {
    newText = newText.toLowerCase();

    final List<MemberData> filteredModelList = new ArrayList<>();
    for (MemberData data : datas) {
        final String text = data.getName().toLowerCase();
        if (text.contains(newText)) {
            filteredModelList.add(data);
        }
    }
    return filteredModelList;
}

private List<MemberData> getTabRowList() {
    List<MemberData> currentItem = new ArrayList<>();
    currentItem.add(new MemberData("Albert", "Albert Sub", R.drawable.your_drawable));
    currentItem.add(new MemberData("Abby", "Abby Text", R.drawable.your_drawable));
    currentItem.add(new MemberData("Brian", "Brian Text", R.drawable.your_drawable));
    currentItem.add(new MemberData("Chris", "Chris Text", R.drawable.your_drawable));
    currentItem.add(new MemberData("Dante", "Dante Text", R.drawable.your_drawable));

//Add more of your dummy data here


        return currentItem;
    }
}

And the Adapter:

public class RecyclerViewTabsAdapter extends RecyclerView.Adapter<RecyclerViewTabsViewHolder> {

private final LayoutInflater mInflater;
private final List<MemberData>mItemList;

public RecyclerViewTabsAdapter(Context context, List<MemberData> itemList) {
    mInflater = LayoutInflater.from(context);
    mItemList = new ArrayList<>(itemList);

}

@Override
public RecyclerViewTabsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    final View view = mInflater.inflate(R.layout.cardview_detail, parent, false);
    return new RecyclerViewTabsViewHolder(view);
}

@Override
public void onBindViewHolder(final RecyclerViewTabsViewHolder holder, final int position) {
    final MemberData data = mItemList.get(position);
    holder.bind(data);
    //TODO: Set onClick here

}

@Override
public int getItemCount() {
    return this.mItemList.size();

}

public void animateTo(List<MemberData> memberDatas) {
    applyAndAnimateRemovals(memberDatas);
    applyAndAnimateAdditions(memberDatas);
    applyAndAnimateMovedItems(memberDatas);
}

private void applyAndAnimateRemovals(List<MemberData> newDatas) {
    for (int i = mItemList.size() - 1; i >= 0; i--) {
        final MemberData data = mItemList.get(i);
        if (!newDatas.contains(data)) {
            removeItem(i);
        }
    }
}

private void applyAndAnimateAdditions(List<MemberData> newDatas) {
    for (int i = 0, count = newDatas.size(); i < count; i++) {
        final MemberData data = newDatas.get(i);
        if (!mItemList.contains(data)) {
            addItem(i, data);
        }
    }
}

private void applyAndAnimateMovedItems(List<MemberData> newDatas) {
    for (int toPosition = newDatas.size() - 1; toPosition >= 0; toPosition--) {
        final MemberData data = newDatas.get(toPosition);
        final int fromPosition = mItemList.indexOf(data);
        if (fromPosition >= 0 && fromPosition != toPosition) {
            moveItem(fromPosition, toPosition);
        }
    }
}

public MemberData removeItem(int position) {
    final MemberData data = mItemList.remove(position);
    notifyItemRemoved(position);
    return data;
}

public void addItem(int position, MemberData data) {
    mItemList.add(position, data);
    notifyItemInserted(position);
}

public void moveItem(int fromPosition, int toPosition) {
    final MemberData data = mItemList.remove(fromPosition);
    mItemList.add(toPosition, data);
    notifyItemMoved(fromPosition, toPosition);
    }
}

And the ViewHolder

public class RecyclerViewTabsViewHolder extends RecyclerView.ViewHolder {

CardView cv;
TextView name;
TextView subText;
ImageView memberPhoto;

public RecyclerViewTabsViewHolder(View itemView){
    super(itemView);
    cv = (CardView)itemView.findViewById(R.id.cv);
    name = (TextView)itemView.findViewById(R.id.person_name);
    subText = (TextView)itemView.findViewById(R.id.person_subtext);
    memberPhoto = (ImageView)itemView.findViewById(R.id.person_photo);
}

public void bind(MemberData data){
    name.setText(data.getName());
    subText.setText(data.getSubText());
    memberPhoto.setImageResource(data.getPhotoId());
    }
}

And finally, MemberData.java

public class MemberData {

String name;
String subText;
int photoId;

public MemberData(String name, String subText, int photoId){
    this.name = name;
    this.subText = subText;
    this.photoId = photoId;
}

public String getName(){
    return name;
}

public String getSubText() {
    return subText;
}

public int getPhotoId() {
    return photoId;
    }
}

The only remaining issue (and I'll save this for another post), is the onClick method and getting the correct position after the list has been filtered.

Hope this helps!

Community
  • 1
  • 1
ImArtVandalay
  • 316
  • 5
  • 11
0

enter image description here

In your adapter class implement filterable and override it's method.

    @Override
public Filter getFilter() {

    Filter filter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            FilterResults filterResults = new FilterResults();

            if(charSequence == null | charSequence.length() == 0){
                filterResults.count = getUserModelListFiltered.size();
                filterResults.values = getUserModelListFiltered;

            }else{
                String searchChr = charSequence.toString().toLowerCase();

                List<UserModel> resultData = new ArrayList<>();

                for(UserModel userModel: getUserModelListFiltered){
                    if(userModel.getUserName().toLowerCase().contains(searchChr)){
                        resultData.add(userModel);
                    }
                }
                filterResults.count = resultData.size();
                filterResults.values = resultData;

            }

            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {

            userModelList = (List<UserModel>) filterResults.values;
            notifyDataSetChanged();

        }
    };
    return filter;
}

In your activity create search view and listen to setOnQueryTextListener.

 @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);

    MenuItem menuItem = menu.findItem(R.id.search_view);

    SearchView searchView = (SearchView) menuItem.getActionView();

    searchView.setMaxWidth(Integer.MAX_VALUE);

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {

            usersAdapter.getFilter().filter(newText);
            return true;
        }
    });



    return  true;
}

full tutorial and source code: Recyclerview with search view

Richard Kamere
  • 749
  • 12
  • 10