RecyclerView优化:DiffUtil的使用

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()
            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() {
    var data: List = 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()
    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"
    }
}

你可能感兴趣的:(RecyclerView优化:DiffUtil的使用)