52

I want to implement search functionality for my RecyclerView. On text changed i want to change the data that are displayed with this widget. Maybe this question has been asked before or is simple, but I don't know how the change the data that is to be shown...

My RecyclerView is defined as follows:

     // 1. get a reference to recyclerView
    mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);

    // 2. set layoutManger
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    // 3. create an adapter
    mAdapter = new ItemsAdapter(itemsData);
    // 4. set adapter
    mRecyclerView.setAdapter(mAdapter);

And the data that I am showing is something like:

   ItemData itemsData[] = { new ItemData("Mary Richards"),
        new ItemData("Tom Brown"),
        new ItemData("Lucy London")          
};

So when when I want to give the adapter another set of data, another array (with one item for example), what should I do?

Sandra
  • 4,239
  • 10
  • 47
  • 80

5 Answers5

105

If you have stable ids in your adapter, you can get pretty good results (animations) if you create a new array containing the filtered items and call

recyclerView.swapAdapter(newAdapter, false);

Using swapAdapter hints RecyclerView that it can re-use view holders. (vs in setAdapter, it has to recycle all views and re-create because it does not know that the new adapter has the same ViewHolder set with the old adapter).

A better approach would be finding which items are removed and calling notifyItemRemoved(index). Don't forget to actually remove the item. This will let RecyclerView run predictive animations. Assuming you have an Adapter that internally uses an ArrayList, implementation would look like this:

// adapter code
final List<ItemData> mItems = new ArrayList(); //contains your items
public void filterOut(String filter) {
   final int size = mItems.size();
   for(int i = size - 1; i>= 0; i--) {
       if (mItems.get(i).test(filter) == false) {
           mItems.remove(i);
           notifyItemRemoved(i);
       }
   }
}

It would perform even better if you can batch notifyItemRemoved calls and use notifyItemRangeRemoved instead. It would look sth like: (not tested)

public void filterOut(String filter) {
   final int size = mItems.size();
   int batchCount = 0; // continuous # of items that are being removed
   for(int i = size - 1; i>= 0; i--) {
       if (mItems.get(i).test(filter) == false) {
           mItems.remove(i);
           batchCount ++;
       } else if (batchCount != 0) { // dispatch batch
           notifyItemRangeRemoved(i + 1, batchCount);
           batchCount = 0;
       }
   }
   // notify for remaining
   if (batchCount != 0) { // dispatch remaining
       notifyItemRangeRemoved(0, batchCount);
   }
}

You need to extend this code to add items that were previously filtered out but now should be visible (e.g. user deletes the filter query) but I think this one should give the basic idea.

Keep in mind that, each notify item call affects the ones after it (which is why I'm traversing the list from end to avoid it). Traversing from end also helps ArrayList's remove method performance (less items to shift).

For example, if you were traversing the list from the beginning and remove the first two items. You should either call

notifyItemRangeRemoved(0, 2); // 2 items starting from index 0

or if you dispatch them one by one

notifyItemRemoved(0);
notifyItemRemoved(0);//because after the previous one is removed, this item is at position 0
yigit
  • 37,683
  • 13
  • 72
  • 58
  • Thank you for your response, I will make adjustments that will suit my needs, but this is what I needed basically. You explained it very well. – Sandra Oct 30 '14 at 10:22
  • 1
    What does "stable ids" mean? My dataset changes but I dont know in what way (some new items might be added and some might be removed) yet the view stays the same, so should I pass true or false to swapAdapter? – tomer.z Jun 10 '16 at 08:06
  • A note on the "stable ids" solution: You don't need to implicitly call `swapAdapter()`. It works fine, yes. But any other solution like using the same adapter, and change the backing list items works just as good, as long as you call `notifyDataSetChanged()`. – msal Jun 28 '18 at 18:11
  • swapAdapter is Great! never knew about this method. After all these years. Thanks! – Vinay W Dec 06 '19 at 10:56
  • sawpAdapter is now removed from the library. Is there a way to switch to a different adapter? – The_Martian Oct 14 '21 at 17:30
28

This is my answer - thanks to Ivan Skoric from his site: http://blog.lovelyhq.com/creating-lists-with-recyclerview-in-android/

I created an extra method inside my adapter class:

public void updateList(List<Data> data) {
    mData = data;
    notifyDataSetChanged();
}

Then each time your data changes, you just call this method passing in your new data and your view should change to reflect it.

Simon
  • 19,658
  • 27
  • 149
  • 217
  • 1
    notifyDataSetChanged() kills all the animations, only good for big lists. – Davideas Jul 27 '16 at 12:50
  • @Davidea ... which is the typical use case of RecyclerView – Marcel Falliere Mar 06 '17 at 10:22
  • A recyclerview only keeps your visible views in memory and release them when you scroll them off your screen. It is highly efficient from a memory perspective in comparison to a listview. They operate similar to a listview. You can read the recyclerview docs here: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html – Simon Mar 07 '17 at 11:18
  • i use a handler to get the data from net and i use this to change data but sometimes it adds data to previous items even if there is no change to data – kamran hatami Jul 09 '17 at 14:51
4

Just re-initialize your adapter:

mAdapter = new ItemsAdapter(newItemsData);

or if you only need to remove add a few specific items rather than a whole list:

mAdapter.notifyItemInserted(position);

or

mAdapter.notifyItemRemoved(position);
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Chad Bingham
  • 32,650
  • 19
  • 86
  • 115
3

If you want to change the complete Adapter in the recycler view. you can just simply set by recycler.setAdapter(myAdapter); It will automatically remove the old adapter from recycler view and replace it with your new adapter.

Andre Classen
  • 3,868
  • 3
  • 26
  • 36
Ebad Ali
  • 31
  • 5
0

As ygit answered, swapAdapter is interesting when you have to change the whole content.

But, in my FlexibleAdapter, you can update the items with updateDataSet. You can even configure the adapter to call notifyDataSetChanged or having synchronization animations (enabled by default). That, because notifyDataSetChanged kills all the animations, but it's good to have for big lists.

Please have a look at the description, demoApp and Wiki pages: https://github.com/davideas/FlexibleAdapter

Davideas
  • 3,226
  • 2
  • 33
  • 51