The RecyclerView library recently added the new SortedList
class. Suppose I have a callback that implements a compare()
method that can change over time, i.e. the underlying Comparator can be switched out. What's the best way to tell the SortedList
to completely resort its data?
Here's my own take on it (written in Kotlin):
list.beginBatchedUpdates()
val indices = (0..list.size() - 1).toArrayList()
while (!indices.isEmpty()) {
val i = indices.first()
val item = list.get(i)
list.recalculatePositionOfItemAt(i)
[suppress("USELESS_CAST_STATIC_ASSERT_IS_FINE")]
indices.remove(list.indexOf(item) as Any) //cast to disambiguate remove()
}
list.endBatchedUpdates()
As you can see, I'm tracking the new index after every call to recalculatePositionOfItemAt()
so every item is resorted only once and no item is skipped.
This works but seems really wasteful because recalculatePositionOfItemAt()
will resize the underlying array twice to remove and then readd the item. indexOf
will then perform a new binary search even though the index is already known.
Edit: This seems to lead to an infinite loop if items compare as equal.
Alternative approach (remove all, then add all):
list.beginBatchedUpdates()
val copy = (list.size() - 1 downTo 0).map { list.removeItemAt(it) }
copy.forEach { list.add(it) }
list.endBatchedUpdates()
Unfortunately, there is no api for this. Just copy the source and add that method yourself. Backing data is just an array so you can call Arrays.sort on it, then call notify data set changed. Tracking changes is not trivial when the whole world changes so not really suitable case for sorted list. Please create a feature request on b.Android.com.
The following method worked for me to switch to a new comparator to re-sort the list. I found the animation much smoother with an additional call to notifyDataSetChanged()
private final List<MyObject> mTmp = new ArrayList<>();
private Comparator<MyObject> mComparator = DEFAULT_COMPARATOR;
private final SortedList<MyObject> mSortedList = new SortedList<>(MyObject.class, new SortedList.Callback<MyObject>() {
@Override
public int compare(MyObject a, MyObject b) {
return mComparator.compare(a, b);
}
// and other methods...
});
public void changeComparator(@NonNull Comparator<MyObject> comp) {
mComparator = comp;
mSortedList.beginBatchedUpdates();
for(int i = 0; i < mSortedList.size(); ++i) {
mTmp.add(mSortedList.get(i));
}
mSortedList.clear();
mSortedList.addAll(mTmp);
mTmp.clear();
mSortedList.endBatchedUpdates();
notifyDataSetChanged();
}
© 2022 - 2024 — McMap. All rights reserved.
beginBatchedUpdates()
,recalculatePositionOfItemAt()
(for each item), andendBatchedUpdates()
, but that's just a guess based upon the API. – Alberthaalberti