RecyclerView使用和原理

围巾🧣 2021年07月21日 476次浏览

RecyclerView 相比 ListView

  1. 性能更优

RecyclerView 通过更有效的视图回收机制减少了内存使用,提升了性能。RecyclerView 仅在需要时创建新的视图,并在滚动时回收和重用旧的视图,从而减少了内存分配和垃圾回收的开销。

  1. 各种布局管理器灵活性更高

RecyclerView 提供了高度可定制的布局管理器(LayoutManager),可以轻松实现各种布局效果,如线性布局、网格布局、瀑布流布局等。也可以通过自定义 LayoutManager 实现更复杂的布局需求。

  1. 更强的可扩展性

RecyclerView 提供了灵活的适配器模式(Adapter)和 ViewHolder 模式,允许开发者更高效地绑定数据和视图

  1. 内置动画支持

RecyclerView 提供了内置的动画 ItemAnimator 支持,如插入、删除和移动项目时的动画效果

  1. 分割线和装饰物

RecyclerView 允许通过 ItemDecoration 类来自定义分割线和装饰物

  1. 事件处理

RecyclerView 的事件处理机制更加灵活。开发者可以通过设置 RecyclerView.OnItemTouchListener 接口来处理复杂的触摸事件,而 ListView 只能处理简单的点击和长按事件。

  1. 数据变化的通知

RecyclerView 的 Adapter 提供了一套完善的通知数据变化的方法,如 notifyItemInserted、notifyItemRemoved、notifyItemChanged 等,能够精细控制每个项目的变化,避免刷新整个列表,提高性能。

三层缓存

RecyclerView 的三层缓存策略包括 View Cache、Recycler Pool 和 Scrap Heap。以下是这三层缓存的具体使用流程:

1. View Cache

View Cache 是 RecyclerView 内部的一个缓存,用于保存刚刚被移出屏幕的 ViewHolder。其主要作用是快速复用这些 ViewHolder,而不需要重新创建。

使用流程:

  • 移出屏幕: 当一个 ViewHolder 移出屏幕时,它首先被放入 View Cache。
  • 复用: 当需要一个新的 ViewHolder 时,RecyclerView 首先检查 View Cache。如果有可用的 ViewHolder,就直接复用。
  • 缓存大小: View Cache 的大小通常有限制(默认是 2 或 5),超过这个限制的 ViewHolder 会被移入 Recycler Pool。

2. Recycler Pool

RecyclerView 有一个内部的 Recycler,它维护了一个回收池,用于存储离开屏幕但仍可重用的 ViewHolder。Recycler Pool 是一个更通用的缓存池,用于保存各种类型的 ViewHolder。它可以跨多个 RecyclerView 共享,用于更大范围的 ViewHolder 复用。

使用流程:

  • View Cache 满: 当 View Cache 满了,多余的 ViewHolder 会被移入 Recycler Pool。
  • 复用: 当 View Cache 没有可用的 ViewHolder 时,RecyclerView 会检查 Recycler Pool。如果有合适的 ViewHolder,就从中取出并复用。
  • 自定义: 开发者可以自定义 Recycler Pool 的行为,例如设置每种 ViewHolder 类型的缓存数量。

3. Scrap Heap

Scrap Heap 是一个临时缓存,用于保存当前屏幕上不可见但仍在屏幕范围内的 ViewHolder。通常用于处理滚动过程中暂时不可见的视图。

使用流程:

  • 临时不可见: 当视图在屏幕范围内但暂时不可见时(例如被覆盖),RecyclerView 会将其 ViewHolder 存入 Scrap Heap。
  • 复用: 当这些视图重新变为可见时,RecyclerView 会首先检查 Scrap Heap。如果有可用的 ViewHolder,就直接复用。
  • 销毁: 当 ViewHolder 离开屏幕完全不可见时,会从 Scrap Heap 中移除,并根据情况放入 View Cache 或 Recycler Pool。

具体使用流程示例

假设有一个列表需要显示 100 个项目,但屏幕只能显示 10 个项目:

  1. 初始显示:
    • RecyclerView 创建并显示前 10 个项目的 ViewHolder,并缓存到 View Cache 中。
  2. 向下滚动:
    • 当第一个项目离开屏幕时,它的 ViewHolder 被放入 View Cache。
    • 第11个项目进入屏幕时,RecyclerView 检查 View Cache,如果有可用的 ViewHolder 就复用,否则从 Recycler Pool 获取或创建新的 ViewHolder。
  3. 缓存满时的处理:
    • 如果 View Cache 已满,多余的 ViewHolder 会被移入 Recycler Pool。
    • 当屏幕上项目频繁进出时,暂时不可见的 ViewHolder 会放入 Scrap Heap,以便快速复用。

基本使用

  1. 实现Adapter和ViewHolder

    继承RecyclerView.Adapter,设置ViewHolder泛型,ViewHolder一般放在Adapter的内部类

  2. 编写ViewHolder和Adapter

    实现Adapter 和ViewHolder的抽象方法

    这里使用ViewBinding,得去build.gradle的android {}标签下开启

    viewBinding {
        enabled = true
    }
    

    Adapter例子:

    class WordAdapter(var dataSet: List<Word>) : RecyclerView.Adapter<WordAdapter.WordViewHolder>() {
    
        override fun getItemCount() = dataSet.size
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder {
            // 可以根据 viewType 生成不同的Holder,让列表内容多样化
            return WordViewHolder.create(parent)
        }
    
        override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
            val item = dataSet[position]
            holder.bind(item.word)
        }
    
    
        class WordViewHolder(private val binding: RecyclerviewItemBinding) :
            RecyclerView.ViewHolder(binding.root) {
    
            fun bind(text: String?) {
                binding.textView.text = text
            }
    
            companion object {
                fun create(parent: ViewGroup): WordViewHolder {
                    val binding = RecyclerviewItemBinding.inflate(LayoutInflater.from(parent.context))
                    return WordViewHolder(binding)
                }
            }
        }
    }
    
  3. RecyclerView设置Adapter

    val wordAdapter = WordAdapter(ArrayList())
    val recyclerview = binding.recyclerview
    recyclerview.adapter = wordAdapter
    
  4. RecyclerView设置布局管理器

    recyclerview.layoutManager = LinearLayoutManager(this)
    

优缺点

优点

有回收和复用,速度快,效率高

缺点

需要额外的空间实现缓存机制,不过问题不大

复用机制

对照着图看代码

流程图

RV复用

时序图

RecyclerView_reuse

回收机制

流程图

RV回收

时序图

RecyclerView_recycle

布局流程

dispatchLayoutStep1

  1. Adapter的更新;
  2. 决定该启动哪种动画;
  3. 保存当前View的信息(getLeft(), getRight(), getTop(), getBottom()等);
  4. 如果有必要,先跑一次布局并将信息保存下来。

dispatchLayoutStep2

真正对子View做布局的地方。

  1. 计算锚点,以锚点开始填充RecyclerView(其实就是执行fill方法)。
  2. 执行fill方法,判断RecyclerView是否还有空间,如果有,执行layoutChunk方法,直至填充满。
  3. layoutChunk方法中,寻找到当前要添加的子view,add到RecyclerView中。
  4. 对子view进行measure和layout。

dispatchLayoutStep3

为动画保存View的相关信息; 触发动画; 相应的清理工作。

其实dispatchLayoutStep3()就是做了一些收尾工作,将一些变量重置,处理下动画。

mState.mLayoutStep

  • 初始化为STEP_START
  • 执行完dispatchLayoutStep1后,mState.mLayoutStep = State.STEP_LAYOUT;
  • 执行完dispatchLayoutStep2后,mState.mLayoutStep = State.STEP_ANIMATIONS;
  • 执行完dispatchLayoutStep3后,mState.mLayoutStep = State.STEP_START;