寫(xiě)在前面的話
在項(xiàng)目中衙荐,我們經(jīng)常用到列表,在以前我們使用RecyclerView
,伴隨著肯定會(huì)有一個(gè)繼承RecyclerView.Adapter
的adapter
但是在這個(gè)adapter中滩届,但是這個(gè)adapter中,我們會(huì)寫(xiě)較多的代碼被啼。在com.android.support:recyclerview-v7:27.1.0
中增加了一個(gè)ListAdapter
,這個(gè)ListAdapter
讓我們使用起來(lái)更加方便帜消。
存在的問(wèn)題
在以前我們是通過(guò)這樣的方式來(lái)進(jìn)行處理
class TestAdapter<T> : RecyclerView.Adapter<TestAdapter.ViewHolder<T>>() {
var TAG = "ADAPTER"
var mData: MutableList<T> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> {
val inflater = LayoutInflater.from(parent.context)
return TestAdapter.ViewHolder(inflater.inflate(R.layout.item_user, parent, false))
}
override fun getItemCount(): Int {
return mData.size
}
override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) {
holder.bind(mData[position])
}
fun add(t: T) {
if (t != null) {
mData.add(t)
} else {
Log.e(TAG, "the data is null")
}
notifyDataSetChanged()
}
fun addList(@NonNull ts: List<T>) {
mData.clear()
mData.addAll(0, ts)
notifyDataSetChanged()
}
fun appendList(@NonNull ts: List<T>) {
mData.addAll(mData.size, ts)
notifyDataSetChanged()
}
fun deleteItem(position: Int){
if (mData.size > position){
mData.removeAt(position)
}
notifyDataSetChanged()
}
class ViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(t: T) {
}
}
}
我們大概都會(huì)做類似以上的一種事情棠枉,但是里面有一個(gè)最大的問(wèn)題,那就是不論是增加(add)
還是刪除券犁,都需要我們自己處理术健,處理后我們會(huì)調(diào)用notifyDataSetChanged()
方法進(jìn)行刷新汹碱。
但是刷新之后我們會(huì)發(fā)現(xiàn)RecyclerView
的動(dòng)畫(huà)消失了粘衬。當(dāng)然為了解決這個(gè)問(wèn)題,我們可以調(diào)用以下方法:
- adapter.notifyItemRangeInserted(position, count);
- adapter.notifyItemRangeRemoved(position, count);
- adapter.notifyItemMoved(fromPosition, toPosition);
- adapter.notifyItemRangeChanged(position, count, payload);
這四種方法是帶動(dòng)畫(huà)咳促。
還有一種解決方式就是使用DiffUtil
DiffUtil
DiffUtil
在 support library 25.1.0 的時(shí)候就引入了稚新,最主要的功能就是處理adapter的更新,其功能
就是比較兩個(gè)數(shù)據(jù)集跪腹,用newList和oldList進(jìn)行比較褂删,得出最小的變化量。也就是說(shuō)我們不需要再無(wú)腦的使用
notifyDataSetChanged()
冲茸。也就是說(shuō)如果我們使用了DiffUtil
屯阀,那么我們不需要去區(qū)別上述的四種調(diào)用方式,
DiffUtil
將自動(dòng)為我們處理然后進(jìn)行調(diào)用轴术。
DiffUtil.ItemCallback
在ListAdapter
的構(gòu)造函數(shù)中难衰,我們需要一個(gè)DiffUtil的回調(diào),當(dāng)然我們一般就使用DiffUtil.ItemCallback
class UserDiffCallback : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User?, newItem: User?): Boolean {
return oldItem?.userId == newItem?.userId
}
override fun areContentsTheSame(oldItem: User?, newItem: User?): Boolean {
return oldItem == newItem
}
}
如上逗栽,我們需重寫(xiě)其中的兩個(gè)方法areItemsTheSame
和areContentsTheSame
areItemsTheSame
areItemsTheSame
提供了兩個(gè)對(duì)象盖袭,需你提供這個(gè)兩個(gè)對(duì)象是否是同一個(gè)對(duì)象。在User對(duì)象中有一個(gè)userId,
其代表了唯一性彼宠,所以這里我就使用了oldItem?.userId == newItem?.userId
鳄虱。這個(gè)可根據(jù)實(shí)際情況自行判斷。
areContentsTheSame
areContentsTheSame
也提供了兩個(gè)對(duì)象凭峡,然后需要你提供這個(gè)兩個(gè)對(duì)象的內(nèi)容是否一致拙已,如果不一致,那么
它就將對(duì)列表進(jìn)行重繪和動(dòng)畫(huà)加載摧冀,反之倍踪,表示你已經(jīng)顯示了這個(gè)對(duì)象的內(nèi)容并且沒(méi)有任何的變化,
那么將不做任何的操作按价。
ListAdapter
與之前的adapter沒(méi)有太多的區(qū)別惭适,就是去掉了我們的數(shù)據(jù)列表mData
和不在使用notifyDataSetChanged
相關(guān)
方法。
class UserAdapter : ListAdapter<User, RecyclerView.ViewHolder>(UserDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return UserAdapter.ViewHolder(inflater.inflate(R.layout.item_user, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ViewHolder) {
holder.bind(getItem(position))
}
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
}
}
}
其實(shí)就是一個(gè)很簡(jiǎn)單的Adapter楼镐,不需要任增加任何新的方法(增癞志、刪、改)框产,無(wú)論是增加還是更新或者刪除凄杯,
我們只需要使用adapter.submitList(List)
方法
但是需要注意一個(gè)問(wèn)題错洁,這個(gè)adapter.submitList(List)
方法中需要提供一個(gè)列表,這個(gè)List必須是一個(gè)新的
列表戒突,也就是說(shuō)屯碴,如果你使用的是一個(gè)已經(jīng)加載了的列表,那么將不會(huì)被加載膊存。
總結(jié)
ListAdapter讓開(kāi)發(fā)著使用更少的代碼,并且能夠讓用戶擁有視覺(jué)上的享受