Skip to content

Instantly share code, notes, and snippets.

@evant
Created November 29, 2016 21:04
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evant/6c207c1af1f1a6c4b1392aa5a8bce9f3 to your computer and use it in GitHub Desktop.
Save evant/6c207c1af1f1a6c4b1392aa5a8bce9f3 to your computer and use it in GitHub Desktop.
RxDiffUtil

Usage

Given a stream of collection of items to show, provide a diff callback which determines the size and differences in that collection.

Observable<List<String>> observable1 ...;
observable1.compose(RxDiffUtil.diff(new RxDiffUtil.Callback<List<String>>() {
    @Override
    public int getSize(List<String> list) {
        return list.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, List<String> oldItems, int newItemPosition, List<String> newItems) {
        return oldItems.get(oldItemPosition).equals(newItems.get(newItemPosition));
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, List<String> oldItems, int newItemPosition, List<String> newItems) {
        return true;
    }
})).subscribe(result -> {
   // This should be on the main thread. Update the adapter's list and dispatch the diff updates.
    adapter.items = result.items;
    result.diff.dispatchUpdatesTo(adapter);
});

If you know your collection is a List, you can more convienetly just provide the item diffing logic.

Observable<List<String>> observable2 = ...;
observable2.compose(RxDiffUtil.diff(new RxDiffUtil.ListCallback<String>() {
    @Override
    public boolean areItemsTheSame(String oldItem, String newItem) {
        return oldItem.equals(newItem);
    }

    @Override
    public boolean areContentsTheSame(String oldItem, String newItem) {
        return true;
    }
})).subscribe(result -> {
    adapter.items = result.items;
    result.diff.dispatchUpdatesTo(adapter);
});

Alternativly, you can implement Diffable on your items and provide the diff logic there. isSame() coresponds to areItemsTheSame() and equals() coresponds to areContentsTheSame().

public static class Foo implements RxDiffUtil.Diffable<Foo> {
    private final int id;
    private final String value;

    public Foo(int id, String value) {
        this.id = id;
        this.value = value;
    }

    @Override
    public boolean isSame(Foo other) {
        return id == other.id;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + value.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Foo foo = (Foo) o;
        if (id != foo.id) return false;
        return value.equals(foo.value);
    }
}

Observable<List<Foo>> observable3 ...;
observable3.compose(RxDiffUtil.<Foo>diff())
        .subscribe(result -> {
            adapter.items = result.items;
            result.diff.dispatchUpdatesTo(adapter);
        });

Feel free to run the diff on a background thread.

Observable<List<Foo>> observable3;
observable3
        .observeOn(Schedulers.computation())
        .compose(RxDiffUtil.<Foo>diff())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(result -> {
            adapter.items = result.items;
            result.diff.dispatchUpdatesTo(adapter);
        });
import android.support.v7.util.DiffUtil;
import java.util.List;
import rx.Observable;
import rx.functions.Func2;
public class RxDiffUtil {
public static <T> Observable.Transformer<T, Result<T>> diff(final Callback<T> callback) {
return diff(callback, true);
}
public static <T> Observable.Transformer<T, Result<T>> diff(final Callback<T> callback, final boolean detectMoves) {
return new Observable.Transformer<T, Result<T>>() {
@Override
public Observable<Result<T>> call(Observable<T> observable) {
return observable.reduce(null, new Func2<Result<T>, T, Result<T>>() {
@Override
public Result<T> call(Result<T> oldResult, T newItems) {
if (oldResult == null) {
return new Result<>(newItems, DiffUtil.calculateDiff(new FirstDiffCallback<>(callback, newItems), detectMoves));
} else {
return new Result<>(newItems, DiffUtil.calculateDiff(new DiffCallback<>(callback, oldResult.items, newItems), detectMoves));
}
}
});
}
};
}
public static <T extends Diffable<T>> Observable.Transformer<List<T>, Result<List<T>>> diff(boolean detectMoves) {
return diff(new DiffableListCallback<T>(), detectMoves);
}
public static <T extends Diffable<T>> Observable.Transformer<List<T>, Result<List<T>>> diff() {
return diff(true);
}
public interface Diffable<T> {
boolean isSame(T other);
}
public static class Result<T> {
public final T items;
public final DiffUtil.DiffResult diff;
public Result(T items, DiffUtil.DiffResult diff) {
this.items = items;
this.diff = diff;
}
}
public static abstract class Callback<T> {
public abstract int getSize(T list);
public abstract boolean areItemsTheSame(int oldItemPosition, T oldItems, int newItemPosition, T newItems);
public abstract boolean areContentsTheSame(int oldItemPosition, T oldItems, int newItemPosition, T newItems);
}
public static abstract class ListCallback<T> extends Callback<List<T>> {
@Override
public int getSize(List<T> list) {
return list.size();
}
@Override
public final boolean areItemsTheSame(int oldItemPosition, List<T> oldItems, int newItemPosition, List<T> newItems) {
return areItemsTheSame(oldItems.get(oldItemPosition), newItems.get(newItemPosition));
}
@Override
public final boolean areContentsTheSame(int oldItemPosition, List<T> oldItems, int newItemPosition, List<T> newItems) {
return areContentsTheSame(oldItems.get(oldItemPosition), newItems.get(newItemPosition));
}
public abstract boolean areItemsTheSame(T oldItem, T newItem);
public abstract boolean areContentsTheSame(T oldItem, T newItem);
}
public static class DiffableListCallback<T extends Diffable<T>> extends ListCallback<T> {
@Override
public boolean areItemsTheSame(T oldItem, T newItem) {
return oldItem.isSame(newItem);
}
@Override
public boolean areContentsTheSame(T oldItem, T newItem) {
return oldItem.equals(newItem);
}
}
private static class FirstDiffCallback<T> extends DiffUtil.Callback {
private final Callback<T> callback;
private final T newItems;
FirstDiffCallback(Callback<T> callback, T newItems) {
this.callback = callback;
this.newItems = newItems;
}
@Override
public int getOldListSize() {
return 0;
}
@Override
public int getNewListSize() {
return callback.getSize(newItems);
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return false;
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return false;
}
}
private static class DiffCallback<T> extends DiffUtil.Callback {
private final Callback<T> callback;
private final T oldItems;
private final T newItems;
DiffCallback(Callback<T> callback, T oldItems, T newItems) {
this.callback = callback;
this.oldItems = oldItems;
this.newItems = newItems;
}
@Override
public int getOldListSize() {
return callback.getSize(oldItems);
}
@Override
public int getNewListSize() {
return callback.getSize(newItems);
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return callback.areContentsTheSame(oldItemPosition, oldItems, newItemPosition, newItems);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return callback.areContentsTheSame(oldItemPosition, oldItems, newItemPosition, newItems);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment