使用RecyclerView動態改變item時遇到的坑

最近想到優化一下本身的應用,用動態改變item代替以前的一股腦notifyDataSetChanged,提高一下速度,並且動態改變還能實現動畫效果。android

老代碼:算法

private fun listItemChanged(v: View? = null) {
    ((v ?: view)!!.list.adapter as RMAdapter).let {
        it.data = getNewData()
        it.notifyDataSetChanged()
    }
}
複製代碼

新代碼:bash

private fun listItemChanged(v: View? = null) {
    ((v ?: view)!!.list.adapter as RMAdapter).let {
        val newData = getNewData()
        val diffResult = DiffUtil.calculateDiff(DiffCallBack(it.data, newData), true)
        diffResult.dispatchUpdatesTo(it)
        it.data = newData
    }
}
複製代碼

能夠看到,這裏使用了一個叫DiffUtil的東西,是在官方的support v7包裏自帶的。功能是檢測兩個數據集之間的差異,而後經過dispatchUpdatesTo幫你調用Adapter裏對應的動態更改item的方法。原理是使用了一個1986年提出的Myers差分算法,讓檢查差異的速度飛快,官方數據是在安卓6.0的Nexus 5X上檢測出1000個項目中的50個改動只要3.5ms。官方頁面ide

具體的使用方式我就不詳細說了,畢竟這不是這篇文章的主題。佈局

因此....目前看上去一切都好?post

遇到的錯位問題

刪除沒有問題,問題是,刪除以後item都錯位了。(其實這花了我挺久才意識到這是錯位而不是什麼其它奇奇怪怪的問題)優化

立馬跑去onBindViewHolder設斷點動畫

//簡化的代碼,只保留了關鍵部分
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    holder.itemView.setOnClickListener {
        openChatActivity(data[position].grokid)  //在此處設斷點後發現position爲1
    }
}
複製代碼

刪除第一項後明明position應該爲0啊,而後我就懵了,這難道是官方bug?spa

去網上搜了一些博文,並無找到什麼解決辦法,甚至有人是在用完DiffUtil後再用notifyDataSetChanged刷新一遍(我:....)code

解決

我最後固然是解決了,不然也沒有這篇文章了

睡了一覺起來後我忽然注意到onBindViewHolder這個名字,這是在設置佈局的時候纔會調用的啊!而設置佈局的時候這一項的確在第二的位置。而動態改變item並不會像**notifyDataSetChanged**那樣所有從新設置一遍及局,因此這裏的老的**position**,用到了新的**data**表上,形成了錯位。

問題找到就很好解決了:在一開始的時候就把值記錄下來,無視以後data表的變化

代碼:

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    val grokid = data[position].grokid      //加上這一行
    holder.itemView.setOnClickListener {
        openChatActivity(grokid)
    }
}
複製代碼

至此,問題解決,能夠美滋滋地享受動畫了


第一次寫文章,請多多指教!

相關文章
相關標籤/搜索