RecyclerView-DiffUtil

1.前言

2.工具类要点

2.1 声明并初始化AsyncListDiffer

private val mDiffer: AsyncListDiffer<T> 

2.2 创建DiffUtil.ItemCallback<T>,实现抽象方法

abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean
abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean

2.3 更新数据

fun setData(list: MutableList<T>?) {
    val newList: MutableList<T> = ArrayList()
    newList.addAll(list ?: ArrayList())
    mDiffer.submitList(newList) // 更新数据(setData()+notify())
}

3. 注意事项

3.1 不能重复提交同一个列表

  • 更新数据前(调用submitList(List<T> newList)),请勿对"已被设置进DiffUtil的list数据"进行操作。
  • 需重新创建list,以便DiffUtil进行两个新旧列表间对比。如举例。

3.2 错误示范

mData.add(...)
mAdapter.submitList(mData)

3.3 引发此问题原因

public class AsyncListDiffer<T> {
    // 更新数据
    public void submitList(@Nullable final List<T> newList, @Nullable final Runnable commitCallback) {
        // incrementing generation means any currently-running diffs are discarded when they finish
        final int runGeneration = ++mMaxScheduledGeneration;
        if (newList == mList) { // 此处进行了过滤
            // nothing to do (Note - still had to inc generation, since may have ongoing work)
            if (commitCallback != null) {
                commitCallback.run();
            }
            return;
        }
        ...
    }
}

4. 基类封装实现

使用DiffUtil进行RecyclerView数据的对比&刷新,把相关api进行二次封装,以便为了更简单调用。

4.1 BaseRecyclerDifferItemCallBack

  • DiffUtil.ItemCallback<T>进行二次封装。
  • RecyclerView的dapter作为参数,把对比函数回调到Adapter进行实现。
class BaseRecyclerDifferItemCallBack<T>(adapter: BaseRecyclerDifferAdapter<T>?) :
        DiffUtil.ItemCallback<T>() {

    private var mAdapter: BaseRecyclerDifferAdapter<T>? = adapter

    override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
        return mAdapter?.areItemsTheSame(oldItem, newItem)?: false
    }

    override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
        return mAdapter?.areContentsTheSame(oldItem, newItem)?: false
    }
}

4.2 BaseRecyclerDifferAdapter

  • RecyclerView.Adapter进行二次封装,初始化AsyncListDiffer变量用于数据操作。
  • 创建数据对比抽象方法,用于此Adapter子类实现
abstract class BaseRecyclerDifferAdapter<T> : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val mDiffer: AsyncListDiffer<T> by lazy {
        // 数据的操作由AsyncListDiffer实现
        AsyncListDiffer(this, BaseRecyclerDifferItemCallBack<T>(this))
    }

    fun getCurrentData(): MutableList<T> {
        return mDiffer.currentList
    }

    /**
     * 设置数据
     * @param list MutableList<T>?
     */
    fun setData(list: MutableList<T>?){
        val newList: MutableList<T> = ArrayList()
        newList.addAll(list?: ArrayList())
        mDiffer.submitList(newList)
    }


    /**
     * 添加数据
     * @param beans Array<out T>
     */
    fun addData(vararg beans: T) {
        val newList: MutableList<T> = ArrayList()
        newList.addAll(mDiffer.currentList)
        beans.forEach { bean ->
            newList.add(bean)
        }
        mDiffer.submitList(newList)
    }

    /**
     * 加载更多
     * @param list MutableList<T>?
     */
    fun loadMore(list: MutableList<T>?){
        val newList: MutableList<T> = ArrayList()
        newList.addAll(mDiffer.currentList)
        newList.addAll(list?: ArrayList())
        mDiffer.submitList(newList)
    }

    /**
     * 删除数据
     * @param index Int
     */
    fun removeData(index: Int) {
        val newList: MutableList<T> = ArrayList()
        val currentList = mDiffer.currentList
        if (currentList.isNotEmpty() && index in 0 until currentList.size) {
            newList.addAll(currentList)
            newList.removeAt(index)
            mDiffer.submitList(newList)
        }
    }

    /**
     * 清空数据
     */
    fun clear() {
        mDiffer.submitList(null)
    }

    abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean
    abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean

4.2 业务Adapter实现

  • 举例说明BaseRecyclerDifferAdapter使用方式
  • 以下为伪代码,仅包含关键实现
class RVDifferAdapter(context: Context) : BaseRecyclerDifferAdapter<RVDifferBean>() {
    
    private var mContext: Context = context

    /**
     * Item对比
     */
    override fun areItemsTheSame(oldItem: RVDifferBean, newItem: RVDifferBean): Boolean {
        return oldItem.id == newItem.id // 比较逻辑自行实现
    }
    
    /**
     * Content对比
     */
    override fun areContentsTheSame(oldItem: RVDifferBean, newItem: RVDifferBean): Boolean {
        return oldItem.name == newItem.name // 比较逻辑自行实现
    }

    private var mInflater: LayoutInflater = LayoutInflater.from(context)

    /**
     * 获取单条item数据
     *
     * @param position
     * @return
     */
    private fun getItemAtPosition(position: Int): RVDifferBean? {
        val itemCount: Int = itemCount
        return if (itemCount != 0 && position in 0 until itemCount) {
            getCurrentData()[position]
        } else {
            null
        }
    }

    override fun getItemCount(): Int {
        return getCurrentData().size
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {}

    override fun onBindViewHolder(
        holder: RecyclerView.ViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        super.onBindViewHolder(holder, position, payloads)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    }
}

参考文档:

官方文档地址:[https://developer.android.google.cn/reference/kotlin/androidx/recyclerview/widget/DiffUtil]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容