Android RecyclerView使用ListAdapter高效刷新數據

原文:Android RecyclerView使用ListAdapter高效刷新數據 - Stars-One的雜貨小窩
我們都知道,當RecyclerView數據源更新后,還需要通過adapter調用對應的方法,從而讓RecyclerView重新繪制頁面
本次也是介紹了用另外一種方法來實現RecyclerView高效刷新數據的功能
問題首先,默認各位是有使用RecyclerView的經驗的,
對于數據的更新,我們一般可以使用adapter的下面四個方法:
  • notifyDataSetChanged() 整個數據改變
  • notifyItemInserted() 往某個下標插入數據,并觸發動畫
  • notifyItemChanged() 更新某個下標的數據,并觸發動畫
  • notifyItemRangeRemoved() 移除某個下標的數據,并觸發動畫
但是,其中下面的三個方法傳參需要給個position下標,這個有時候每次由我們去計算獲取,很麻煩,而且我們還要處理對應的增刪改的邏輯
所以之后Android官方也是出了一個新的工具DiffUtils
DiffUtils使用DiffUtil主要提供了一個靜態方法供我們調用calculateDiff(),其中的參數為一個Callback靜態抽象類,我們需要先寫一個類,繼承并實現其中的方法
class DiffCallBack(val oldList: ArrayList<Person>, val newList: ArrayList<Person>) :DiffUtil.Callback() {    //判斷兩個對象是否相同    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    }    //判斷兩個對象內容是否相同    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {        val newItem = newList[newItemPosition]        val oldItem = oldList[oldItemPosition]        //如果新數據和舊數據的名稱和年齡相同,則視為兩個item的內容相同        return oldItem.age == newItem.age && oldItem.name == newItem.name    }}實際上,此類就是用來比較兩個List的不同之處,定義區分兩個同類的對象,是否相同,從上面的兩個方法也是能夠看得出來
首先,areItemsTheSame()方法先判斷兩個item是否為同個對象
這里我是選用了id作為唯一標識來區分是否為同一對象,當然,也可以用內存地址來比對,如果是內存地址來比對,則涉及淺拷貝和深拷貝的問題,這里不擴展講解了
其次,再通過areContentsTheSame()方法來判斷兩個item內容是否相同
現在,我們有了一個Callback類,可以使用calculateDiff()方法了:
val oldList = adapter.getData()//深拷貝oldList得到newList,然后對newList按照業務進行增刪改的操作,這里代碼就省略了..//計算不同之處val diffResult = DiffUtil.calculateDiff(DiffCallBack(oldList,newList))//adapter設置新數據adapter.setData(newList)//將變更操作分發給adapterdiffResult.dispatchUpdatesTo(adapter)上面給的代碼可能不是太全,因為這種方法不是我們推薦的寫法,更推薦使用ListAdapter來實現此功能,具體可看下文
實際上,DiffUtil算法還是耗時間的,如果數據更多,估計時間也會隨之增多,所以,官方推薦開啟個異步線程來處理計算,之后分發操作再切換UI線程進行數據的更新操作
ListAdapter使用ListAdapter其實就是對上面的DiffUtil的一個封裝類,以往,我們的Adapter都是繼承了RecyclerView.Adapter,并在其中寫了個List去裝載數據,十分麻煩
ListAdapter里面維護著線程池并且還會為我們將視圖修改操作移到主線程,這樣我們就可以很方便的使用DiffUtil了
如果我們將此Adapter替換成繼承與ListAdapter,那么都不需要我們在類中寫上個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 {                //如果新數據和舊數據的名稱和年齡相同,則視為兩個item的內容相同                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()    }}

推薦閱讀