原生的 ObservableArrayList 不支持 move 操作, 所以我们需要自定义 BaseObservableList
BaseObservableList 代码如下
package org.atoto.autotest.base
import android.annotation.SuppressLint
import androidx.databinding.ListChangeRegistry
import androidx.databinding.ObservableList
class BaseObservableList<T> : ArrayList<T>(),
ObservableList<T> {
@Transient
private var mListeners: ListChangeRegistry? = ListChangeRegistry()
override fun addOnListChangedCallback(listener: ObservableList.OnListChangedCallback<out ObservableList<T>>?) {
if (mListeners == null) {
mListeners = ListChangeRegistry()
}
mListeners?.add(listener)
}
override fun removeOnListChangedCallback(listener: ObservableList.OnListChangedCallback<out ObservableList<T>>?) {
mListeners?.remove(listener)
}
override fun add(element: T): Boolean {
super.add(element)
notifyAdd(size - 1, 1)
return true
}
override fun add(index: Int, element: T) {
super.add(index, element)
notifyAdd(index, 1)
}
override fun addAll(elements: Collection<T>): Boolean {
val oldSize = size
val added = super.addAll(elements)
if (added) {
notifyAdd(oldSize, size - oldSize)
}
return added
}
override fun addAll(index: Int, elements: Collection<T>): Boolean {
val added = super.addAll(index, elements)
if (added) {
notifyAdd(index, elements.size)
}
return added
}
override fun clear() {
val oldSize = size
super.clear()
if (oldSize != 0) {
notifyRemove(0, oldSize)
}
}
override fun removeAt(index: Int): T {
val value = super.removeAt(index)
notifyRemove(index, 1)
return value
}
override fun remove(element: T): Boolean {
val index = indexOf(element)
if (index >= 0) {
removeAt(index)
return true
} else {
return false
}
}
fun move(fromIndex: Int, toIndex: Int) {
val value = super.removeAt(fromIndex)
super.add(toIndex, value)
mListeners?.notifyMoved(this, fromIndex, toIndex, 1)
}
override fun set(index: Int, element: T): T {
val value = super.set(index, element)
mListeners?.notifyChanged(this, index, 1)
return value
}
override fun removeRange(fromIndex: Int, toIndex: Int) {
super.removeRange(fromIndex, toIndex)
notifyRemove(fromIndex, toIndex - fromIndex)
}
private fun notifyAdd(start: Int, count: Int) {
if (mListeners != null) {
mListeners?.notifyInserted(this, start, count)
}
}
private fun notifyRemove(start: Int, count: Int) {
if (mListeners != null) {
mListeners?.notifyRemoved(this, start, count)
}
}
}
配合使用的 BaseBindingAdapter
BaseBindingAdapter 代码如下:
package org.atoto.autotest.base
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ObservableList
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
abstract class BaseBindingAdapter<M, B : ViewDataBinding>(private var items: BaseObservableList<M>) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
class BaseBindingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
private val dataListChangedCallback = ListChangedCallback()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val binding: B = DataBindingUtil.inflate(LayoutInflater.from(parent.context), getLayoutResId(), parent, false)
return BaseBindingViewHolder(binding.root)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is BaseBindingViewHolder) {
val bean : M = items[position]
val binding: B? = DataBindingUtil.getBinding(holder.itemView)
onBindItem(binding, bean, position)
binding?.executePendingBindings()
}
}
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
items.addOnListChangedCallback(dataListChangedCallback)
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
items.removeOnListChangedCallback(dataListChangedCallback)
}
override fun getItemCount() = if (items.isEmpty()) 0 else items.size
abstract fun getLayoutResId(): Int
abstract fun onBindItem(binding: B?, bean: M, position: Int)
/**
* 额外监听数据变化,这样就可以直接更新数据,
* 而不用每次都调用notifyDataSetChanged()等方式更新数据
*
*
* 我只对插入和移除做了监听处理,其他方式没有处理
* 小伙伴可以根据自己的具体需要做额外处理
*/
inner class ListChangedCallback : ObservableList.OnListChangedCallback<ObservableList<M>>() {
override fun onChanged(newItems: ObservableList<M>) {
notifyDataSetChanged()
}
override fun onItemRangeChanged(newItems: ObservableList<M>, positionStart: Int, itemCount: Int) {
notifyItemRangeChanged(positionStart, itemCount ,"")
}
override fun onItemRangeInserted(newItems: ObservableList<M>, positionStart: Int, itemCount: Int) {
notifyItemRangeInserted(positionStart, itemCount)
}
override fun onItemRangeMoved(newItems: ObservableList<M>, fromPosition: Int, toPosition: Int, itemCount: Int) {
Log.d("[BaseBindingAdapter]", "onItemRangeMoved fromPosition:$fromPosition toPosition:$toPosition itemCount:$itemCount")
if (itemCount == 1) {
notifyItemMoved(fromPosition, toPosition)
} else {
notifyDataSetChanged()
}
}
override fun onItemRangeRemoved(newItems: ObservableList<M>, positionStart: Int, itemCount: Int) {
notifyItemRangeRemoved(positionStart, itemCount)
}
}
}
public fun <T> baseObservableListOf(vararg elements: T): BaseObservableList<T> =
if (elements.isEmpty()) BaseObservableList() else {
val baseObservableList = BaseObservableList<T>()
for (element in elements) {
baseObservableList.add(element)
}
baseObservableList
}