Infinum Android Talks #20 - DiffUtil

Post on 08-Apr-2017

149 views 0 download

Transcript of Infinum Android Talks #20 - DiffUtil

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 ZELJKO.PLESAC@INFINUM.CO