DiffUtil
ŽELJKO PLESAC
TASK : UPDATE THE RECYCLER VIEW’S CONTENT.
Sounds like an easy task…
1. MISSING APIS
RecyclerAdapter doesn’t have ArrayAdapter’s like methods -
add(), addAll(), remove(), clear()…
MISSING APIS
DEFINE CUSTOM RECYCLER ADAPTER.
private List<E> items;
public void add(E item) {…}public void addAll(Collection<E> collection) {…}public void add(E item, int index) {…}public void addAll(Collection<E> collection, int index) {…} public void remove(E item) {…} public void removeAll(Collection<E> collection) {…} public void remove(int index) {…}
public void clear() {…}
Can add and remove items from adapter!
2. UPDATE THE CONTENT OF AN ADAPTER.
We need to manually fetch the correct item from adapter and
update it’s content.
Need to handle edge cases - what happens if item is not
presented in adapter?
Blocks the UI thread.
PROBLEMS WITH UPDATE
DIFF UTIL TO THE RESCUE.
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.
DIFF UTIL
Uses Myers's difference algorithm to calculate the minimal
number of updates to convert one list into another.
Myers's algorithm does not handle items that are moved so
DiffUtil runs a second pass on the result to detect items that
were moved.
DIFF UTIL
If the lists are large, this operation may take significant time.
Use it on a background thread.
DIFF UTIL
public class SportsBookDiffUtils extends DiffUtil.Callback {
… @Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { Bundle bundle = new Bundle(); bundle.putInt(EXTRA_NUMBER_OF_EVENTS, newList.get(newItemPosition).getEventCount()); return bundle; } @Override public int getOldListSize() { return oldList != null ? oldList.size() : 0; } @Override public int getNewListSize() { return newList != null ? newList.size() : 0; } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { return newList.get(newItemPosition).getSportId() == oldList.get(oldItemPosition).getSportId(); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { return newList.get(newItemPosition).getEventCount() == oldList.get(oldItemPosition).getEventCount(); }}
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new SportsBookDiffUtils(oldSports, newSports));diffResult.dispatchUpdatesTo(sportsAdapter);
@Overridepublic void onBindViewHolder(MjolnirRecyclerAdapter<Sport>.ViewHolder holder, int position, List<Object> payloads) { if (!payloads.isEmpty()) { Bundle bundle = (Bundle) payloads.get(0); int eventCount = bundle.getInt(SportsBookDiffUtils.EXTRA_NUMBER_OF_EVENTS, 1); eventsTextView.setText(String.format( SettingsUtils.getLocale(), getContext().getString(R.string.events_format), eventCount)); } else { eventsTextView.setText(String.format( SettingsUtils.getLocale(), getContext().getString(R.string.events_format), get(position).getEventCount()) ); } }
Can add, remove and update the content!
It will block the UI (calculate the result on background thread
and, update on main).
Actual runtime of the algorithm significantly depends on the
number of changes in the list and the cost of your comparison
methods (more here).
Due to implementation constraints, the max size of the list can
be 2^26.
DRAWBACKS?
BONUS
INFINUM REINVENTS THE RECYCLER VIEW
ONE RECYCLER VIEW TO RULE THEM ALL
Provides a simple way to extend the default RecyclerView
behaviour with support for headers, footers, empty view,
DiffUtil and ArrayAdapter like methods.
Open sourced until Ragnarök.
MJOLNIR RECYCLERVIEW
Thank you!
Visit www.infinum.co or find us on social networks:
infinum.co infinumco infinumco infinum
@ZELJKOPLESAC [email protected]