/** * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or * {@link Callback#onMoved(int, int)} if necessary. * * <p>You can use this method if you need to change an existing Item such that its position in the * list may change. * * <p>If the new object is a different object (<code>get(index) != item</code>) and {@link * Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList avoids * calling {@link Callback#onChanged(int, int)} otherwise it calls {@link Callback#onChanged(int, * int)}. * * <p>If the new position of the item is different than the provided <code>index</code>, * SortedList calls {@link Callback#onMoved(int, int)}. * * @param index The index of the item to replace * @param item The item to replace the item at the given Index. * @see #add(Object) */ public void updateItemAt(int index, T item) { throwIfMerging(); final T existing = get(index); // assume changed if the same object is given back boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item); if (existing != item) { // different items, we can use comparison and may avoid lookup final int cmp = mCallback.compare(existing, item); if (cmp == 0) { mData[index] = item; if (contentsChanged) { mCallback.onChanged(index, 1); } return; } } if (contentsChanged) { mCallback.onChanged(index, 1); } // TODO this done in 1 pass to avoid shifting twice. removeItemAtIndex(index, false); int newIndex = add(item, false); if (index != newIndex) { mCallback.onMoved(index, newIndex); } }
/** Ends the update transaction and dispatches any remaining event to the callback. */ public void endBatchedUpdates() { throwIfMerging(); if (mCallback instanceof BatchedCallback) { ((BatchedCallback) mCallback).dispatchLastEvent(); } if (mCallback == mBatchedCallback) { mCallback = mBatchedCallback.mWrappedCallback; } }
/** Removes all items from the SortedList. */ public void clear() { throwIfMerging(); if (mSize == 0) { return; } final int prevSize = mSize; Arrays.fill(mData, 0, prevSize, null); mSize = 0; mCallback.onRemoved(0, prevSize); }
/** * This method can be used to recalculate the position of the item at the given index, without * triggering an {@link Callback#onChanged(int, int)} callback. * * <p>If you are editing objects in the list such that their position in the list may change but * you don't want to trigger an onChange animation, you can use this method to re-position it. If * the item changes position, SortedList will call {@link Callback#onMoved(int, int)} without * calling {@link Callback#onChanged(int, int)}. * * <p>A sample usage may look like: * * <pre> * final int position = mSortedList.indexOf(item); * item.incrementPriority(); // assume items are sorted by priority * mSortedList.recalculatePositionOfItemAt(position); * </pre> * * In the example above, because the sorting criteria of the item has been changed, * mSortedList.indexOf(item) will not be able to find the item. This is why the code above first * gets the position before editing the item, edits it and informs the SortedList that item should * be repositioned. * * @param index The current index of the Item whose position should be re-calculated. * @see #updateItemAt(int, Object) * @see #add(Object) */ public void recalculatePositionOfItemAt(int index) { throwIfMerging(); // TODO can be improved final T item = get(index); removeItemAtIndex(index, false); int newIndex = add(item, false); if (index != newIndex) { mCallback.onMoved(index, newIndex); } }
/** * Batches adapter updates that happen between calling this method until calling {@link * #endBatchedUpdates()}. For example, if you add multiple items in a loop and they are placed * into consecutive indices, SortedList calls {@link Callback#onInserted(int, int)} only once with * the proper item count. If an event cannot be merged with the previous event, the previous event * is dispatched to the callback instantly. * * <p>After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()} which * will dispatch any deferred data change event to the current callback. * * <p>A sample implementation may look like this: * * <pre> * mSortedList.beginBatchedUpdates(); * try { * mSortedList.add(item1) * mSortedList.add(item2) * mSortedList.remove(item3) * ... * } finally { * mSortedList.endBatchedUpdates(); * } * </pre> * * <p>Instead of using this method to batch calls, you can use a Callback that extends {@link * BatchedCallback}. In that case, you must make sure that you are manually calling {@link * BatchedCallback#dispatchLastEvent()} right after you complete your data changes. Failing to do * so may create data inconsistencies with the Callback. * * <p>If the current Callback in an instance of {@link BatchedCallback}, calling this method has * no effect. */ public void beginBatchedUpdates() { throwIfMerging(); if (mCallback instanceof BatchedCallback) { return; } if (mBatchedCallback == null) { mBatchedCallback = new BatchedCallback(mCallback); } mCallback = mBatchedCallback; }
/** * Adds the given items to the list. Equivalent to calling {@link SortedList#add} in a loop, * except the callback events may be in a different order/granularity since addAll can batch them * for better performance. * * <p>If allowed, may modify the input array and even take the ownership over it in order to avoid * extra memory allocation during sorting and deduplication. * * @param items Array of items to be added into the list. * @param mayModifyInput If true, SortedList is allowed to modify the input. * @see {@link SortedList#addAll(Object[] items)}. */ public void addAll(T[] items, boolean mayModifyInput) { throwIfMerging(); if (items.length == 0) { return; } if (mayModifyInput) { addAllInternal(items); } else { T[] copy = (T[]) Array.newInstance(mTClass, items.length); System.arraycopy(items, 0, copy, 0, items.length); addAllInternal(copy); } }
/** * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}. * * @param index The index of the item to be removed. * @return The removed item. */ public T removeItemAt(int index) { throwIfMerging(); T item = get(index); removeItemAtIndex(index, true); return item; }
/** * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}. * * @param item The item to be removed from the list. * @return True if item is removed, false if item cannot be found in the list. */ public boolean remove(T item) { throwIfMerging(); return remove(item, true); }
/** * Adds the given item to the list. If this is a new item, SortedList calls {@link * Callback#onInserted(int, int)}. * * <p>If the item already exists in the list and its sorting criteria is not changed, it is * replaced with the existing Item. SortedList uses {@link Callback#areItemsTheSame(Object, * Object)} to check if two items are the same item and uses {@link * Callback#areContentsTheSame(Object, Object)} to decide whether it should call {@link * Callback#onChanged(int, int)} or not. In both cases, it always removes the reference to the old * item and puts the new item into the backing array even if {@link * Callback#areContentsTheSame(Object, Object)} returns false. * * <p>If the sorting criteria of the item is changed, SortedList won't be able to find its * duplicate in the list which will result in having a duplicate of the Item in the list. If you * need to update sorting criteria of an item that already exists in the list, use {@link * #updateItemAt(int, Object)}. You can find the index of the item using {@link #indexOf(Object)} * before you update the object. * * @param item The item to be added into the list. * @return The index of the newly added item. * @see {@link Callback#compare(Object, Object)} * @see {@link Callback#areItemsTheSame(Object, Object)} * @see {@link Callback#areContentsTheSame(Object, Object)}} */ public int add(T item) { throwIfMerging(); return add(item, true); }