I have some problems understanding RecyclerView
s SortedList
.
Lets say I have a very simple class only having a very simple class holding data:<
I had a similar issue when creating a chat app which I need to update messages by their IDs and sort them by their dates. The support lib's SortedList does not do that or at least I had little time to dive into its source code and test. So, I created a small component, MultiSortedList:
import android.support.v7.widget.RecyclerView
/**
* Created by abduaziz on 6/14/18.
*
* MultiSortedList is a wrapper component to ArrayList that keeps its elements in a sorted order
* using UpdateCallbackInterface. It is intended to be used inside recycler view adapters.
*
* */
class MultiSortedList(var updateCallback: UpdateCallback, var adapter: RecyclerView.Adapter<*>? = null) {
companion object {
val TAG = "SORTEDLIST"
}
// internal list to hold elements by sortBy() -> visible to user
private val list: ArrayList = arrayListOf()
// internal list to hold elements by updateBy() -> not visible
private val uList: ArrayList = arrayListOf()
// add adapter from ui
fun addAdapter(adapter: RecyclerView.Adapter<*>?) {
this.adapter = adapter
}
/*
* 1. Search for existing element that satisfies updateBy()
* 2. Remove the existing element if found
* 3. Add the new item with sortBy()
* 4. Notify if adapter is not null
* */
fun add(newItem: T) {
remove(newItem)
// save to internal list by updateBy()
var toBeStoredPosition = uList.binarySearch { updateCallback.updateBy(it, newItem) }
if (toBeStoredPosition < 0) toBeStoredPosition = -(toBeStoredPosition + 1)
uList.add(toBeStoredPosition, newItem)
// save to UI list and notify changes
var sortPosition = list.binarySearch { updateCallback.sortBy(it, newItem) }
if (sortPosition < 0) sortPosition = -(sortPosition + 1)
list.add(sortPosition, newItem)
adapter?.notifyItemInserted(sortPosition)
}
/*
* Remove and notify the adapter
* */
fun remove(removeItem: T) {
val storedElementPosition = uList.binarySearch { updateCallback.updateBy(it, removeItem) }
if (storedElementPosition >= 0 && storedElementPosition < uList.size) {
// remove from internal list
val itemTobeRemoved = uList[storedElementPosition]
uList.removeAt(storedElementPosition)
// remove from ui
val removePosition = list.binarySearch { updateCallback.sortBy(it, itemTobeRemoved) }
if (removePosition >= 0 && removePosition < list.size) {
list.removeAt(removePosition)
adapter?.notifyItemRemoved(removePosition)
}
}
}
// can be accessed -> list.get(position) or list[position]
operator fun get(pos: Int): T {
return list[pos]
}
// for adapter use
fun size(): Int {
return list.size
}
inline fun forEachIndexed(action: (Int, T) -> Unit) {
for (index in 0 until size()) {
action(index, get(index))
}
}
/*
* UpdateCallback is the main interface that is used to compare the elements.
* - sortBy() is used to locate new elements passed to SortedList
* - updateBy() is used to update/remove elements
*
* Typical example would be Message model class which we want to:
* - Sort messages according to their dates
* - Update/Remove messages according to their randomIDs or IDs.
* */
interface UpdateCallback {
fun sortBy(i1: T, i2: T): Int
fun updateBy(oldItem: T, newItem: T): Int
}
}
The usage is explained here: https://medium.com/@abduazizkayumov/sortedlist-with-recyclerview-part-2-64c3e9b1b124