kotlin databinding 如何触发 notifyMoved 移动元素操作

87 阅读2分钟

原生的 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
}