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

Pegasis發表於2019-01-06

最近想到優化一下自己的應用,用動態改變item代替之前的一股腦notifyDataSetChanged,提升一下速度,而且動態改變還能實現動畫效果。

老程式碼:

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

新程式碼:

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。官方頁面

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

所以….目前看上去一切都好?

遇到的錯位問題

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

立馬跑去onBindViewHolder設斷點

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

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

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

解決

我最後當然是解決了,否則也沒有這篇文章了

睡了一覺起來後我突然注意到onBindViewHolder這個名字,這是在設定佈局的時候才會呼叫的啊!而設定佈局的時候這一項的確在第二的位置。而動態改變item並不會像**notifyDataSetChanged**那樣全部重新設定一遍佈局,所以這裡的老的**position**,用到了新的**data**表上,造成了錯位。

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

程式碼:

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

至此,問題解決,可以美滋滋地享受動畫了


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

來源:https://juejin.im/post/5c31b389518825261b7df86e#comment

相關文章