RecyclerView相比ListView已经有局部的定向更新,但是如果涉及到大量的数据的更新,比如执行刷新操作后,正常需要替换所有的数据,然后调用
adapter.notifyDataSetChanged()
进行全量更新;但是可能出现界面闪烁;另外大多数时候有一部分数据其实没有变化,理论上并不需要重新绑定;
DiffUtil使用场景
- 存在更新前和更新后的两个数据集合可以用来对比,如果直接操作Adapter.Data则没法使用;
- 避免更新页面闪烁
- 部分数据刷新
- 可以覆写
areContentsTheSame、areItemsTheSame
两个方法,比如areContentsTheSame
需要比较内容,可能需要JavaBean重写equals方法
性能对比
The test list is composed of random UUID Strings and the tests are run on Nexus 5X with M
- 100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms
- 100 items and 100 modifications: 3.82 ms, median: 3.75 ms
- 100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms
- 1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms
- 1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms
- 1000 items and 200 modifications: 27.07 ms, median: 26.92 ms
- 1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms
DiffUtil使用的算法
算法看不懂,可参考这个 https://www.jianshu.com/p/7f1473c2e521
用例
class RecyclerViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recyclerview)
supportActionBar!!.title = "RecyclerView"
add.setOnClickListener {
val data = ArrayList<User>()
for (i in 42..100) {
data.add(User("name $i", i))
}
val calculateDiff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return recyclerView.adapter?.itemCount ?: 0
}
override fun getNewListSize(): Int {
return data.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return data[newItemPosition] == (recyclerView.adapter as RecyclerViewAdapter).data[oldItemPosition]
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return data[newItemPosition].name === (recyclerView.adapter as RecyclerViewAdapter).data[oldItemPosition].name
}
})
(recyclerView.adapter as RecyclerViewAdapter).data = data
calculateDiff.dispatchUpdatesTo(recyclerView.adapter!!)
}
recyclerView.initRecyclerView()
}
}
class RecyclerViewAdapter : RecyclerView.Adapter<ViewHolder>() {
var data: List<User> = ArrayList()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val contentView = LayoutInflater.from(parent.context).inflate(R.layout.adapter_item_recyclerview, parent, false)
return object : ViewHolder(contentView) {}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.tv_text.text = data[position].toString()
holder.itemView.setOnClickListener { v -> Toast.makeText(v.context, "点击:$position", Toast.LENGTH_SHORT).show() }
}
override fun getItemCount(): Int {
return data.size
}
}
internal fun RecyclerView.initRecyclerView() {
this.layoutManager = GridLayoutManager(this.context, 2, RecyclerView.VERTICAL, false)
val verticalItemDecoration = DividerItemDecoration(this.context, DividerItemDecoration.VERTICAL)
val horizontalItemDecoration = DividerItemDecoration(this.context, DividerItemDecoration.HORIZONTAL)
verticalItemDecoration.setDrawable(this.context.resources.getDrawable(android.R.drawable.divider_horizontal_textfield))
horizontalItemDecoration.setDrawable(this.context.resources.getDrawable(android.R.drawable.divider_horizontal_textfield))
this.addItemDecoration(verticalItemDecoration)
this.addItemDecoration(horizontalItemDecoration)
val adapter = RecyclerViewAdapter()
val data = ArrayList<User>()
for (i in 0..59) {
data.add(User("name $i", i))
}
adapter.data = data
this.adapter = adapter
adapter.notifyDataSetChanged()
}
data class User(var name: String, var age: Int) {
override fun toString(): String {
return "$name : $age"
}
}
网友评论