Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use SortedList in RecyclerView with Android data binding library?

Android data binding provides several Observable data interfaces including ObservableList. But SortedList (introduced in recent version of RecyclerView library) does not extend List at all.

How could I use SortedList for RecyclerView with Android data binding library?

like image 893
Oasis Feng Avatar asked Jul 09 '15 03:07

Oasis Feng


2 Answers

Inspired by George Mount, I have implemented my version of ObservableSortedList with full functionalities from the original SortedList, including:

  • Batch update. Just call beginBatchedUpdates() and endBatchedUpdates() as of SortedList.
  • De-dup and smart refresh. The callback in the constructor is responsible for ordering, de-dup and content differentiation.

Full code:

public class ObservableSortedList<T> extends AbstractList<T> implements ObservableList<T> {

  /** @see android.support.v7.util.SortedList.Callback */
  public interface Callback<T2> {
    /** @see android.support.v7.util.SortedList.Callback#compare(Object, Object) */
    int compare(T2 o1, T2 o2);
    /** @see android.support.v7.util.SortedList.Callback#areItemsTheSame(Object, Object) */
    boolean areItemsTheSame(T2 item1, T2 item2);
    /** @see android.support.v7.util.SortedList.Callback#areContentsTheSame(Object, Object) */
    boolean areContentsTheSame(T2 oldItem, T2 newItem);
  }

  public ObservableSortedList(final Class<T> klass, final Callback<T> callback) {
    mList = new SortedList<>(klass, new CallbackWrapper<>(callback));
  }

  /** @see SortedList#beginBatchedUpdates() */
  public void beginBatchedUpdates() { mList.beginBatchedUpdates(); }
  /** @see SortedList#endBatchedUpdates() */
  public void endBatchedUpdates() { mList.endBatchedUpdates(); }

  @Override public boolean add(final T item) {
    sTlsUpdated.set(false);
    mList.add(item);
    return sTlsUpdated.get();   // May be set by Callback.onInserted() or onChanged().
  }

  @Override public T set(final int location, final T object) {
    final T old = mList.get(location);
    mList.updateItemAt(location, cast(object));
    return old;
  }

  @Override public int indexOf(final Object object) {
    try {
      return mList.indexOf(cast(object));
    } catch (final ClassCastException ignored) {
      return -1;
    }
  }

  @Override public boolean remove(final Object object) {
    try {
      return mList.remove(cast(object));
    } catch (final ClassCastException ignored) {
      return false;
    }
  }

  @SuppressWarnings("unchecked") private T cast(final Object object) { return (T) object; }

  @Override public boolean contains(final Object object) { return indexOf(object) != SortedList.INVALID_POSITION; }
  @Override public T get(final int location) { return mList.get(location); }
  @Override public int size() { return mList.size(); }
  @Override public void clear() { mList.clear(); }
  @Override public T remove(final int location) { return mList.removeItemAt(location); }

  /* ObservableList */

  @Override public void addOnListChangedCallback(final OnListChangedCallback<? extends ObservableList<T>> callback) {
    if (mListeners == null) this.mListeners = new ListChangeRegistry();
    mListeners.add(callback);
  }

  @Override public void removeOnListChangedCallback(final OnListChangedCallback<? extends ObservableList<T>> callback) {
    if (mListeners == null) return;
    mListeners.remove(callback);
  }

  private final SortedList<T> mList;
  private static final ThreadLocal<Boolean> sTlsUpdated = new ThreadLocal<>();
  private transient @Nullable ListChangeRegistry mListeners = new ListChangeRegistry();

  public class CallbackWrapper<T2> extends SortedList.Callback<T2> {

    @Override public final void onInserted(final int position, final int count) {
      sTlsUpdated.set(true);
      if (mListeners != null) mListeners.notifyInserted(ObservableSortedList.this, position, count);
    }

    @Override public final void onRemoved(final int position, final int count) {
      if (mListeners != null) mListeners.notifyRemoved(ObservableSortedList.this, position, count);
    }

    @Override public final void onMoved(final int fromPosition, final int toPosition) {
      if (mListeners != null) mListeners.notifyMoved(ObservableSortedList.this, fromPosition, toPosition, 1);
    }

    @Override public final void onChanged(final int position, final int count) {
      sTlsUpdated.set(true);
      if (mListeners != null) mListeners.notifyChanged(ObservableSortedList.this, position, count);
    }

    @Override public int compare(final T2 o1, final T2 o2) { return mCallback.compare(o1, o2); }
    @Override public boolean areContentsTheSame(final T2 oldItem, final T2 newItem) { return mCallback.areContentsTheSame(oldItem, newItem); }
    @Override public boolean areItemsTheSame(final T2 item1, final T2 item2) { return mCallback.areItemsTheSame(item1, item2); }
    public CallbackWrapper(final Callback<T2> callback) { mCallback = callback; }

    private final Callback<T2> mCallback;
  }
}
like image 164
Oasis Feng Avatar answered Nov 19 '22 06:11

Oasis Feng


To make a SortedList support Observability in data binding, you must implement the interface yourself. Since there is conflict in the API, it appears that you'll have to wrap it. Something like this:

public class ObservableSortedList<T> implements ObservableList<T> {
    private final SortedList<T> mList;
    private final ListChangeRegistry mListeners = new ListChangeRegistry();

    @Override
    public boolean add(T item) {
        int index = mList.add(item);
        mListeners.notifyInserted(this, index, 1);
    }

    @Observable
    public void addOnListChangedCallback(OnListChangedCallback<? extends ObservableList<T>> callback) {
        mListeners.add(callback);
    }

    ...
}
like image 30
George Mount Avatar answered Nov 19 '22 06:11

George Mount