15

I need to implement search functionality inside my android app which uses toolbar, SlidingTabLayout and ViewPager that holds fragments. Inside each fragment there is a RecyclerView with list of items.

RecyclerView data is static defined in separate class (DataAccess.java) and those lists are updated and RecyclerView gets refreshed just by calling (without passing new data)

mRecyclerView.getAdapter().notifyDataSetChanged();

Is there any simple way to temporary filter RecyclerView without changing the data and after the user presses return button inside Toolbar to remove the filter and show inital list.

Before pressing Search icon inside toolbar menu:

enter image description here

So when the user is typing "Josip..." the filter will be active

enter image description here

and after he presses the X button in SearchView the user will get the same data as before without filter.

@Override 
public boolean onQueryTextChange(String newText) {
    // filter data (temporary remove all items from DataAccess.list that don't .startsWith(newText)
}

@Override 
public boolean onQueryTextSubmit(String query)
    // Doesn't help if I revert deleted items here
}
fpopic
  • 1,756
  • 3
  • 28
  • 40

3 Answers3

24
@Override 
public boolean onQueryTextSubmit(String query){
    ((ItemAdapter) myRecList.getAdapter()).setFilter(query)
}

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {

    private List<String> visibleObjects;
    private List<String> allObjects;

    .....

    public void flushFilter(){
        visibleObjects=new ArrayList<>();
        visibleObjects.addAll(allObjects);
        notifyDataSetChanged();
    }

    public void setFilter(String queryText) {

        visibleObjects = new ArrayList<>();
        constraint = constraint.toString().toLowerCase();
        for (String item: allObjects) {            
                if (item.toLowerCase().contains(queryText))                     
                    visibleObjects.add(item);            
        }
       notifyDataSetChanged();
    }
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
merovingen
  • 423
  • 3
  • 7
  • 1
    IN @Override public boolean onQueryTextChange(String newText) { if (text.isEmpty()) ((ItemAdapter) myRecList.getAdapter).flushFilter(); } OR IN OnActionExpandListener.onMenuItemActionCollapse(MenuItem item) – merovingen Jan 06 '15 at 13:09
  • searchView.setOnCloseListener() is the thing, thanks anyway your comment put me on the right way ! – fpopic Jan 06 '15 at 16:44
  • 2
    @fpopic Could you post your solution? – Jared Burrows May 20 '15 at 05:18
  • I didn't have time to finish it cause answers came to late, but have you tried this @merovingen answer, it seems fine? – fpopic May 20 '15 at 05:25
3

I wanted to add as comment but due to less reputation...I am answering post. This method works fine if (item.toLowerCase().contains(queryText)) but what to do if match is not found in first iteration.then it will go in else part without looping throughout allObjects list...

for (RouteByATMList.Route_ATM item: Main_ATMItemList)
        {

            if (item.ATMNumber.contains(queryText)) {
                visibleObjects.add(item);
            }else {
                Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
                break;
            }
        }

I got the answer from my superior ,hope it helps.

public void setFilter(String queryText) {
        visibleObjects = new ArrayList<>();
        for (RouteByATMList.Route_ATM item: Main_ATMItemList)
        {
            if (item.ATMNumber.contains(queryText))
            {
                visibleObjects.add(item);
            }
        }

        if(visibleObjects.size()==0){
            Toast.makeText(mContext,"No search result found!",Toast.LENGTH_SHORT).show();
        }

        notifyDataSetChanged();
        Log.e("dataset changed","dataset changed");
    }
Ravi Yadav
  • 2,296
  • 3
  • 25
  • 32
3

I don't oppose the given and accepted answer. There is a room for possible performance pitfalls. One should make use of Filterabe interface. Having this implemented will behave as ol'good ListView that did the filtering asynchronously. Then all you need is to write your own Filter and instantiate it in the overridden getFilter() method;

In my case I used Filter to sort an adapter of many (yeah, many many) items. It was junky sorting it on UI-thread.

So I had this abstract class

public abstract class BaseFilterableRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> implements Filterable {

    private Context mContext;
    public BaseFilterableRecyclerViewAdapter(Context context) {
        this.mContext = context;
    }
    public abstract void sort(SortingFilter.Sort sortingStrategy);

    //...Other methods

}

And the class that inherit:

public class ItemAdapter extends BaseFilterableRecyclerViewAdapter<RecyclerView.ViewHolder>{


  //... RecyclerView methods


  @Override
    public Filter getFilter() {
        return new SortingFilter(mData) {
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results.values != null) {
                    int last = mData.size();
                    mData = (List<Product>) results.values;
                    notifyDataSetChanged();
                }
            }
        };
    }



    @Override
    public void sort(SortingFilter.Sort sortingStrategy) {
        getFilter().filter(sortingStrategy.toString());
    }

}
Nikola Despotoski
  • 49,966
  • 15
  • 119
  • 148