以往我们在使用
RecyclerView
时最常使用的是
RecyclerView.Adapter
及其更新方式notifyDataSetChanged()
。
此篇会介绍新的Adapter -ListAdapter
以及新的更新方法 -DiffUtil
ListAdapter
+DiffUtil
在动态更新上,
效能比以往大提升,撰写上也方便且省code。
我们以前使用notifyDataSetChanged()
在每次更新时,
都会去重新绑定list中的每一笔item的viewHolder
,
以及重新绘制item的view
(包含未显示在萤幕上的item)的耗能。
此外notifyDataSetChanged()
也容易会造成大量萤幕更新,
显示上会使user看到萤幕闪烁、滚动时的弹跳。
而为了解决这件事,会使用RecyclerView提供的工具来取代notifyDataSetChanged()
:
notifyItemInserted(position)
notifyItemChanged(position)
notifyItemMoved(fromPosition, toPosition)、
notifyItemRemoved(position)
notifyItemRangeInserted(position, count)
notifyItemRangeChanged(position, count)
notifyItemRangeRemoved(position, count)
notifyItemRangeChanged(position, count, payload)
手动找出list修改的部分,以进行新增、删除、移动、更新那些变动的item。
现在,我们可以使用DiffUtil
自带的演算法,
搭配ListAdapter
来帮你搞定一切更新。
DiffUtil
是为了改善RecyclerView的更新效能而生。
他能自动帮你判断新进来的资料与旧资料是否相同、移动、新增、删除⋯⋯等等。
ListAdapter
是继承RecyclerView.Adapter
的adapter:
- 优点1: 它包含了初始化
DiffUtil.ItemCallback
的方式在里面- 优点2: 它内建了
submitList(list)
的function,
可以取代每次我们在RecyclerView.Adapter
中都需要撰写用来更新的dataList- 优点3: 不需再覆写
getItemCount()
,ListAdapter
会帮你计算好size
//用ListAdapter就不用再每次重写他了
var dataList = listOf<T>()
set(value) {
field = value
notifyDataSetChanged()
}
接下来我们就来下实作范例:
ListAdapter
以及DiffUtil.ItemCallback<DataClass>
(以Person
为DataClass
作范例)。
先新增一个Person
作为本文范例list的DataClass
:
data class Person(
val id: Long,
val name: String
)
改写上我们将原本的RecyclerView.Adapter
class RvAdapter() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
替换为
class RvAdapter() :
ListAdapter<Person, RecyclerView.ViewHolder>(DiffCallback())
就可以了。
注:
DiffCallback()
是DiffUtil.ItemCallback
的class新增DiffCallback
class,extends DiffUtil.ItemCallback<DataClass>()
,
并implement需要覆写的方法:
class DiffCallback : DiffUtil.ItemCallback<Person>() {
//1
override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean {
TODO("Not yet implemented")
}
//2
override fun areContentsTheSame(oldItem: Person, newItem: Person): Boolean {
TODO("Not yet implemented")
}
}
1.areItemsTheSame
用来判断item是否被编辑、移动、或删除,
在这边我们使用id
来作为判断。
(注:如果id
一样的话,他会被当成一样的item来判断)
此方法能使DiffUtil
自动帮我们判断这笔id现在位置在哪,
来使用最佳方法更新数据及展示相对应的更新动画。
2.areContentsTheSame
是为了避免在发生更改时重新设计整个列表,
只会更新两个列表之间具有不同值的项目。
class DiffCallback : DiffUtil.ItemCallback<Person>() {
override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Person, newItem: Person): Boolean {
return oldItem == newItem
}
}
areItemsTheSame
用来确认新旧item是否有相同id
(也可以是不同的值)
areContentsTheSame
来确认新旧item内容是否完全相等。
以往取值方式为:
var dataList = listOf<Person>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val name = dataList.getOrNull(position)
}
使用ListAdapter
,直接用getItem
就可取得值:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val name = getItem(position)
}
在ListAdapter
中我们拔除了以前拿来更新用的dataList,
提交新list时,只要使用submitList
,让DiffUtil
演算法来计算就可以了。
//activity、fragment
viewModel.personListResult.observe(viewLifecycleOwner, { resultList ->
rvAdapter.submitList(resultList)
})
全程序码范例展示:
(有时间来补上github连结给各位)
data class Person(
val id: Long?,
val name: String?
)
class PersonAdapter : ListAdapter<Person, RecyclerView.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ItemViewHolder.from(parent)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ItemViewHolder -> {
holder.bind(getItem(position))
}
}
}
class ItemViewHolder private constructor(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(item: Person) {
itemView.tv_play_name.text = item.name
}
companion object {
fun from(parent: ViewGroup): RecyclerView.ViewHolder {
val binding = LayoutInflater.from(parent.context)
.inflate(R.layout.itemview_person, parent, false)
return ItemViewHolder(binding)
}
}
}
class DiffCallback: DiffUtil.ItemCallback<Person>() {
override fun areItemsTheSame(oldItem: Person, newItem: Person): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Person, newItem: Person): Boolean {
return oldItem == newItem
}
}
}
参考资源:
<<: [13th-铁人赛]Day 2:Modern CSS 超详细新手攻略 - 入门
写到最後三天了,想要聊聊和 Android 不完全相关的东西。感谢JetBrains 的开发,和 K...
MVC模式(Model–view–controller) 是软件工程中的一种软件架构模式, 把软件...
终於最後一天,照惯例大家都在这里写後记吧~ 三年前第一次知道铁人赛,就是以社群身份去参加了颁奖典礼X...
前言: 前两遍的基础activity 和 fragment 就可以作出不错的app了,但功能愈来愈多...
接下来讲讲Entity Framework 如何建立... 首先先开启visual studio.....