Android?RecyclerView使用ListAdapter高效刷新數(shù)據(jù)的操作方法
我們都知道,當(dāng)RecyclerView數(shù)據(jù)源更新后,還需要通過(guò)adapter調(diào)用對(duì)應(yīng)的方法,從而讓RecyclerView重新繪制頁(yè)面
本次也是介紹了用另外一種方法來(lái)實(shí)現(xiàn)RecyclerView高效刷新數(shù)據(jù)的功能
問(wèn)題
首先,默認(rèn)各位是有使用RecyclerView的經(jīng)驗(yàn)的,
對(duì)于數(shù)據(jù)的更新,我們一般可以使用adapter的下面四個(gè)方法:
- notifyDataSetChanged() 整個(gè)數(shù)據(jù)改變
- notifyItemInserted() 往某個(gè)下標(biāo)插入數(shù)據(jù),并觸發(fā)動(dòng)畫(huà)
- notifyItemChanged() 更新某個(gè)下標(biāo)的數(shù)據(jù),并觸發(fā)動(dòng)畫(huà)
- notifyItemRangeRemoved() 移除某個(gè)下標(biāo)的數(shù)據(jù),并觸發(fā)動(dòng)畫(huà)
但是,其中下面的三個(gè)方法傳參需要給個(gè)position下標(biāo),這個(gè)有時(shí)候每次由我們?nèi)ビ?jì)算獲取,很麻煩,而且我們還要處理對(duì)應(yīng)的增刪改的邏輯
所以之后Android官方也是出了一個(gè)新的工具DiffUtils
DiffUtils使用
DiffUtil主要提供了一個(gè)靜態(tài)方法供我們調(diào)用calculateDiff(),其中的參數(shù)為一個(gè)Callback靜態(tài)抽象類,我們需要先寫(xiě)一個(gè)類,繼承并實(shí)現(xiàn)其中的方法
class DiffCallBack(val oldList: ArrayList<Person>, val newList: ArrayList<Person>) :DiffUtil.Callback() {
//判斷兩個(gè)對(duì)象是否相同
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
//判斷兩個(gè)對(duì)象內(nèi)容是否相同
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val newItem = newList[newItemPosition]
val oldItem = oldList[oldItemPosition]
//如果新數(shù)據(jù)和舊數(shù)據(jù)的名稱和年齡相同,則視為兩個(gè)item的內(nèi)容相同
return oldItem.age == newItem.age && oldItem.name == newItem.name
}
}
實(shí)際上,此類就是用來(lái)比較兩個(gè)List的不同之處,定義區(qū)分兩個(gè)同類的對(duì)象,是否相同,從上面的兩個(gè)方法也是能夠看得出來(lái)
首先,areItemsTheSame()方法先判斷兩個(gè)item是否為同個(gè)對(duì)象
這里我是選用了id作為唯一標(biāo)識(shí)來(lái)區(qū)分是否為同一對(duì)象,當(dāng)然,也可以用內(nèi)存地址來(lái)比對(duì),如果是內(nèi)存地址來(lái)比對(duì),則涉及淺拷貝和深拷貝的問(wèn)題,這里不擴(kuò)展講解了
其次,再通過(guò)areContentsTheSame()方法來(lái)判斷兩個(gè)item內(nèi)容是否相同
現(xiàn)在,我們有了一個(gè)Callback類,可以使用calculateDiff()方法了:
val oldList = adapter.getData() //深拷貝oldList得到newList,然后對(duì)newList按照業(yè)務(wù)進(jìn)行增刪改的操作,這里代碼就省略了.. //計(jì)算不同之處 val diffResult = DiffUtil.calculateDiff(DiffCallBack(oldList,newList)) //adapter設(shè)置新數(shù)據(jù) adapter.setData(newList) //將變更操作分發(fā)給adapter diffResult.dispatchUpdatesTo(adapter)
上面給的代碼可能不是太全,因?yàn)檫@種方法不是我們推薦的寫(xiě)法,更推薦使用ListAdapter來(lái)實(shí)現(xiàn)此功能,具體可看下文
實(shí)際上,DiffUtil算法還是耗時(shí)間的,如果數(shù)據(jù)更多,估計(jì)時(shí)間也會(huì)隨之增多,所以,官方推薦開(kāi)啟個(gè)異步線程來(lái)處理計(jì)算,之后分發(fā)操作再切換UI線程進(jìn)行數(shù)據(jù)的更新操作
ListAdapter使用
ListAdapter其實(shí)就是對(duì)上面的DiffUtil的一個(gè)封裝類,以往,我們的Adapter都是繼承了RecyclerView.Adapter,并在其中寫(xiě)了個(gè)List去裝載數(shù)據(jù),十分麻煩
ListAdapter里面維護(hù)著線程池并且還會(huì)為我們將視圖修改操作移到主線程,這樣我們就可以很方便的使用DiffUtil了
如果我們將此Adapter替換成繼承與ListAdapter,那么都不需要我們?cè)陬愔袑?xiě)上個(gè)List,代碼示例如下:
class RvAdapter() : ListAdapter<Person, RvAdapter.ViewHolder>(diffCallback) {
companion object {
val diffCallback = object : DiffUtil.ItemCallback<Person>() {
override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Person, newItem: Person): Boolean {
//如果新數(shù)據(jù)和舊數(shù)據(jù)的名稱和年齡相同,則視為兩個(gè)item的內(nèi)容相同
return oldItem.age == newItem.age && oldItem.name == newItem.name
}
}
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tvAge: TextView = itemView.findViewById(R.id.tvAge)
var tvName: TextView = itemView.findViewById(R.id.tvUserName)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = View.inflate(parent.context, R.layout.rv_item_person, null)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
holder.tvName.text = item.name
holder.tvAge.text = item.age.toString()
}
}
ListAdapter<T,ViewHolder>第一個(gè)泛型即為你的數(shù)據(jù)實(shí)體類,第二個(gè)參數(shù)為ViewHolder類
注意: 之后的數(shù)據(jù)增刪改查都需要調(diào)用adapter提供的submitList()方法即可
val oldList = adapter.currentList
val newList = oldList.map { it }.toMutableList()
newList.removeAt(10)
//下標(biāo)2加個(gè)新數(shù)據(jù)
newList.add(2, Person(90, "我的", 72))
adapter.submitList(list)
效果:

參考文獻(xiàn)
- 別再notifyDataSetChanged()了!使用DiffUtil讓你的RecyclerView更加絲滑 - 掘金
- 拒絕手動(dòng)Notifydatasetchanged(),使用ListAdapter高效完成RecyclerView刷新 - 掘金
- Android高性能列表:RecyclerView + DiffUtil - 知乎
- Android中DiffUtil的使用詳解 Android開(kāi)發(fā)之DiffUtil的使用詳解(IT技術(shù))
到此這篇關(guān)于Android RecyclerView使用ListAdapter高效刷新數(shù)據(jù)的文章就介紹到這了,更多相關(guān)Android RecyclerView刷新數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義TextBanner實(shí)現(xiàn)自動(dòng)滾動(dòng)
這篇文章主要為大家詳細(xì)介紹了Android自定義TextBanner實(shí)現(xiàn)自動(dòng)滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
Android studio創(chuàng)建第一個(gè)app
這篇文章主要為大家詳細(xì)介紹了如何使用Android studio創(chuàng)建你的第一個(gè)項(xiàng)目Hello World,感興趣的小伙伴們可以參考一下2016-05-05
Android Studio 運(yùn)行時(shí)出現(xiàn)的警告信息解決辦法
這篇文章主要介紹了Android Studio 運(yùn)行時(shí)出現(xiàn)的警告信息解決辦法的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android SpringAnimation彈性動(dòng)畫(huà)解析
這篇文章主要為大家詳細(xì)介紹了Android SpringAnimation彈性動(dòng)畫(huà),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Android 游戲開(kāi)發(fā)中繪制游戲觸摸軌跡的曲線圖
本文主要介紹 Android 繪制游戲觸摸軌跡的曲線圖的簡(jiǎn)單示例,這里詳細(xì)說(shuō)明使用方法,并附示例代碼,有需要的小伙伴可以參考下2016-08-08
Android 文件操作詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了 Android 文件操作詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-02-02
Android 通過(guò)API獲取數(shù)據(jù)庫(kù)中的圖片文件方式
這篇文章主要介紹了Android 通過(guò)API獲取數(shù)據(jù)庫(kù)中的圖片文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android實(shí)戰(zhàn)教程第六篇之一鍵鎖屏應(yīng)用問(wèn)題解決
這篇文章主要為大家詳細(xì)介紹了Android一鍵鎖屏應(yīng)用開(kāi)發(fā)過(guò)程中出現(xiàn)問(wèn)題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11

