0

I tried to use DiffUtil approach to update my list which always consist of 30 items, now each item data updates every minute but no way of telling if all item's data will have updates so to avoid abusing notifyDataSetChanged() I create a class extending DiffUtil.

public class DifUtil extends DiffUtil.Callback {

private final List<Asset> oldList, newList;

public DifUtil(List<Asset> newList, List<Asset> oldList) {
    this.oldList = oldList;
    this.newList = newList;
}

@Override
public int getOldListSize() {
    return oldList.size();
}

@Override
public int getNewListSize() {
    return newList.size();
}

@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
    return oldList.get(oldItemPosition).getId().equals(newList.get(newItemPosition).getId());
}

@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
    return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}

@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
    //you can return particular field for changed item.
    return super.getChangePayload(oldItemPosition, newItemPosition);
}

}

Add new public function to notify the adapter with this

public void updateList(List<Asset> newList) {
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DifUtil(newList, this.assetList));
    this.assetList.clear();
    this.assetList.addAll(newList);
    diffResult.dispatchUpdatesTo(this);
}

Overriding another onBindViewHolder (not sure if needed when not using payload)

onBindViewHolder(@NonNull AssetsAdapter.ViewHolder holder, int position, @NonNull List<Object> payloads)

Then updating the list by just calling

adapter.updateList(newAssetList);

The updating of list works but I can only see those new values by scrolling the list, I need to view the updates even without recycling the view (when scrolling) just like notifyItemChanged().

To my understanding calling dispatchUpdatesTo should handle and update the views and its data or am I missing something here please enlighten me.

  • You have to make copy of `assetList` as diffutil parameter. It's a bit counterintuitive but not entire logic is finalized during `calculateDiff`, if you modifying any of the lists passed there as arguments `dispatchUpdatesTo` might fail. Also do not override 3 argument `onBindViewHolder` if you're not generating and handling payloads. – Pawel Feb 21 '21 at 15:54
  • @Pawel what is the usage of payload anyway? Is it some extra data that you may wanted to pass when a data set change meaning it's completely optional? – Undefined function Feb 21 '21 at 16:19
  • 1
    You can use it to execute partial update of already laid out viewholder: https://stackoverflow.com/questions/33176336/need-an-example-about-recyclerview-adapter-notifyitemchangedint-position-objec/38796098 – Pawel Feb 21 '21 at 17:44

1 Answers1

1

I managed to make it work by following this blog and its way of implementing DiffUtil. https://www.journaldev.com/20873/android-recyclerview-diffutil

However I did not perform the cloning part since it is irrelevant on my case.

BONUS There is a known bug where updates makes the list to scroll at the bottom. I get rid of this by saving scroll state with LayoutManager like in this SO answer.

https://stackoverflow.com/a/44053550/11338467

So my adapter DiffUtil update part will be this

public void updateList(List<Asset> newList) {
    Parcelable recyclerViewState = layoutManager.onSaveInstanceState();
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DifUtil(newList, this.assetList));
    diffResult.dispatchUpdatesTo(this);
    this.assetList.clear();
    this.assetList.addAll(newList);
    layoutManager.onRestoreInstanceState(recyclerViewState);
}