74

When I have to use a classic adapter with a ListView, I update my data in the ListView like this:

myAdapter.swapArray(data);

public swapArray(List<Data> data) {
  clear();
  addAll(data);
  notifyDataSetChanged();
}

I would like to know what is the best practice for a RecyclerView. Because in a RecyclerView adapter you can't do a clear and addAll as in ListView.

So I tried just with a notifyDataSetChanged, but it didn't work. Then I tried with a swapAdapter on my view:

List<Data> data = newData;

MyRecyclerAdapter adapter = new MyRecyclerAdapter(data);

// swapAdapter on my recyclerView (instead of a .setAdapter like with a classic listView).
recyclerViewList.swapAdapter(adapter, false);

But with this last solution, I still have to create a new instance of my adapter and I feel like it's not the best solution. I should be able just to change my data without a new MyRecyclerAdapter.

ישו אוהב אותך
  • 22,515
  • 9
  • 59
  • 80
AlexDG
  • 1,739
  • 2
  • 18
  • 33
  • 2
    It is better (for animations and performance) if you dispatch detailed notify events (e.g. notfiyItemInserted/Removed/Moved/Changed). Also take a look at SortedList class, which can calculate these values for you if you have a sorting criteria in your list. – yigit May 06 '15 at 04:46
  • I'm working on every change in my adapter notifyDataSetChanged(); – marlonpya Dec 23 '16 at 22:52
  • `notifyDataSetChanged()` is the **worst** method for performance. Use it as the last chance. See https://stackoverflow.com/questions/31367599/how-to-update-recyclerview-adapter-data for `notifyItemRangeInserted/Removed/Moved/Changed` as @yigit advised. – CoolMind Apr 16 '19 at 08:53

4 Answers4

114

RecyclerView's Adapter doesn't come with many methods otherwise available in ListView's adapter. But your swap can be implemented quite simply as:

class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
   List<Data> data;
   ...

    public void swap(ArrayList<Data> datas)
    {
        data.clear();
        data.addAll(datas);
        notifyDataSetChanged();     
    }
}

Also there is a difference between

list.clear();
list.add(data);

and

list = newList;

The first is reusing the same list object. The other is dereferencing and referencing the list. The old list object which can no longer be reached will be garbage collected but not without first piling up heap memory. This would be the same as initializing new adapter everytime you want to swap data.

inmyth
  • 8,142
  • 4
  • 38
  • 45
  • Though the above one works for simple list.size(), of data's mine was bit complex where i have multiple list inside the recyclerview. The recycler view list views have managed by multiple view type. Hence it was hard to remove subset of list items that is not visible or not bind by bindViewholder. The only way which is worked for me is RecyclerView.swapAdapter(); – EngineSense Apr 03 '16 at 07:57
  • The above mention swap(ArrayList datas) doesn't work and neither the Log.d comment method RecyclerView.swapAdapter(); ,anyway found my own way to clear the datasource(ArrayList at the creation place) first ,next clear the adapter datasource using method in adapter and again create new datasource(ArrayList) with new data and than adding that data to the adapter arraylist and finally notifydatasetchanged() method. – blackjack Feb 09 '17 at 06:07
  • @inmyth possible edit in your answer above: should "notifyDataSetChanged();" be "data.notifyDataSetChanged();" ? – AJW May 01 '17 at 02:21
  • I don't see why you wouldn't allow a data set that's empty to be loaded. What if i want to clear out my RecyclerView? That `datas.size==0` shouldn't really be there. – Nathan F. Dec 20 '18 at 01:15
  • My answer has been edited many times by many different people. You are right, the only check needed is just for null. – inmyth Dec 20 '18 at 01:57
  • but null check is not supposed to be the scope of the adapter. Which should be null, which should be empty is the responsibility of the data provider. If there's no consistency here expect every single element throughout the app to have null check, which is messed up. – inmyth Dec 20 '18 at 02:14
  • i need a kotlin answer – Jevon Oct 01 '20 at 19:29
22

@inmyth's answer is correct, just modify the code a bit, to handle empty list.

public class NewsAdapter extends RecyclerView.Adapter<...> {    
    ...
    private static List mFeedsList;
    ...    
    public void swap(List list){
            if (mFeedsList != null) {
                mFeedsList.clear();
                mFeedsList.addAll(list);
            }
            else {
                mFeedsList = list;
            }
            notifyDataSetChanged();
    }

I am using Retrofit to fetch the list, on Retrofit's onResponse() use,

adapter.swap(feedList);
Ionut Negru
  • 5,577
  • 4
  • 44
  • 73
Akshay Goyal
  • 811
  • 9
  • 11
  • Hi i'm struggling to use this code in my project where use retrofit to get data from an api, would you mind helping me? – Jevon Oct 01 '20 at 18:58
  • clear and addAll is red for me with the Kotlin translation. – Jevon Oct 01 '20 at 19:16
13

DiffUtil can the best choice for updating the data in the RecyclerView Adapter which you can find in the android framework. DiffUtil is a utility class that can calculate the difference between two lists and output a list of update operations that converts the first list into the second one.

Most of the time our list changes completely and we set new list to RecyclerView Adapter. And we call notifyDataSetChanged to update adapter. NotifyDataSetChanged is costly. DiffUtil class solves that problem now. It does its job perfectly!

Ngima Sherpa
  • 740
  • 7
  • 25
4

Found following solution working for my similar problem:

private ExtendedHashMap mData = new ExtendedHashMap();
private  String[] mKeys;

public void setNewData(ExtendedHashMap data) {
    mData.putAll(data);
    mKeys = data.keySet().toArray(new String[data.size()]);
    notifyDataSetChanged();
}

Using the clear-command

mData.clear()

is not nessescary

termigrator
  • 129
  • 1
  • 9