RecyclerView源码分析

本文详细解析了RecyclerView的绘制流程、缓存机制及刷新机制。包括OnMeasure、OnLayout、OnDraw的过程,四级缓存机制的优点和策略,以及Recycler的局部刷新机制。

1.宏观认识

RecyclerView是谷歌官方出的一个用于大量数据展示的新控件,支持RecyclerView高效运行有六个类.

属性名称功能描述
Adapter为每一项Item创建视图
ViewHolder承载Item视图的子布局
LayoutManager负责Item视图的布局的显示管理
ItemDecoration给每一项Item视图添加子View,例如可以进行画分隔线之类
ItemAnimator负责处理数据添加或者删除时候的动画效果
Recycler负责RecyclerView中子View的回收与复用

Adapter:主要负责ViewHolder的创建以及数据变化时通知RecycledView进行视图的更新。
Adapter类中有一个数据源集合dataSource的引用
getItemCount()用来告诉RecyclerView展示的总条目
它并不是直接映射data -> ViewHolder, 而是 data position -> data type -> viewholder。 所以对于ViewHolder来说,它知道的只是它的view type
ViewHolder:
对于Adapter来说,一个ViewHolder就对应一个data。它也是Recycler缓存池的基本单元。
在这里插入图片描述
itemView : 会被当做child view来add到RecyclerView中。
mPosition : 标记当前的ViewHolder在Adapter中所处的位置。
mItemViewType : 这个ViewHolder的Type,在ViewHolder保存到RecyclerPool时,主要靠这个类型来对ViewHolder做复用。
mFlags : 标记ViewHolder的状态,用在刷新上,比如 FLAG_BOUND(显示在屏幕上)、FLAG_INVALID(无效,想要使用必须rebound)、FLAG_REMOVED(已被移除)等。

/****************************************************************/
RecyclerView的职责就是将Datas中的数据以一定的规则展示在它的上面,但本质上RecyclerView只是一个ViewGroup,它只认识View,不清楚Data数据的具体结构。因此,RecyclerView需要一个Adapter来与Datas进行交流。Adapter的工作就是将Data转换为RecyclerView认识的ViewHolder
在这里插入图片描述

图 1-1
此外,Recycler将自己作为View的 onMeasure()、onLayout()直接交给LayoutManager,onDraw()交给了ItemDecoration来绘制。为了高效的管理ViewHold,引入了Recycler对ViewHold进行缓存管理。
到了这里,有负责翻译数据的Adapter,有负责布局的LayoutManager,有负责管理View的Recycler,一切都很完美,但RecyclerView为了更加人性化,引入了ItemAnimator来添加和控制ViewHold添加和删除的动画效果。最终的关系图如1-2所示
在这里插入图片描述

图 1-2

2.绘制的源码解析

参考文章

2.1.OnMeasure :与布局流程相互结合在一起的

在这里插入图片描述

onMeasure方法的代码很长,但是总的就分为三种情况:
没有LayoutManager的情况
有LayoutManager并开启自动测量
有LaoutManager但没有开启自动测量

在 没有LayoutManager的情况下,就是执行了defaultOnMeasure方法,里面就是计算并设置了RecyclerView的长宽值,不处理子VIew了

void defaultOnMeasure(int widthSpec, int heightSpec) {
    // calling LayoutManager here is not pretty but that API is already public and it is better
    // than creating another method since this is internal.
    final int width = LayoutManager.chooseSize(widthSpec,
            getPaddingLeft() + getPaddingRight(),
            ViewCompat.getMinimumWidth(this));
    final int height = LayoutManager.chooseSize(heightSpec,
            getPaddingTop() + getPaddingBottom(),
            ViewCompat.getMinimumHeight(this));

    setMeasuredDimension(width, height);
}

一般都会设置LayoutManager。从源码中看出不论是否启用 mAutoMeasure 最终都会执行到 mLayout.onMeasure() 方法中,而这个 mLayout 就是一个 LayoutManager 对象。
常用的三个LayoutManager,在其构造函数中,均已经开启了自动测量,所以我们可以放心地为RecyclerView设置wrap_content。
AutoMeasure是先通过LayoutManager的onLayoutChildren方法来实现子view的测量和布局,然后在获取子view的尺寸和位置来确定RecyclerView的尺寸


自动测绘过程可以分为两部分:

第一部分:
首先执行LayoutManager的onMeasure方法。(defaultOnMeasure)先要有一个值(这个值在xml文件中直接设置的)
检查如果width和height都已经是精确值(xxdp),那么就不用再根据内容进行计算所需要的width和height,那么跳过之后的步骤。如果有其中任何一个值不是精确值(wrap_content),则进入到下面计算所需长宽的步骤。
第二部分:
开启布局流程(依次调用dispatchLayoutStep1,2)(在step2中调用子类的onLayoutChildren()),计算出所有Child的边界。
然后根据计算出的Child的边界计算出RecyclerView的所需width和height,并设置。
检查是否需要再次测量,如果需要则在此进行测量。
PS: State是在RecyclerView中定义的静态内部类,用来保存RecyclerView的状态,像目标滚动的位置,视图的焦点等.

2.2.OnLayout()

 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
        dispatchLayout();
        TraceCompat.endSection();
        mFirstLayoutComplete = true;
    }

核心是dispatchLayout()方法。在dispatchLayout()中主要有3个步骤
RecyclerView的布局过程分为3步:dispatchLayoutStep1,dispatchLayoutStep2和dispatchLayoutStep3。在之前自动化Measure过程中我们为了得到Child的边界值,使用了dispatchLayoutStep1和dispatchLayoutStep2;
/********************************************************/
DispatchLayoutStep1():
记录RecyclerView刷新前ItemView的各种信息,如Top,Left,Bottom,Right,用于动画的相关计算
DispatchLayoutStep2():
做出呈现在手机上的试图效果
具体过程:调用实现子类的onLayoutChildern()函数,计算所有子View的边界值,根据计算出的Child的边界计算出RecyclerView的所需width和height,并设置。

DispatchLayoutStep3():
保持当前子视图的动画信息,在必要时进行触发或者清除

/************************************************************/

onLayout总结

  1. RecyclerView并没有对内部的View进行布局。而是交给LayoutManager去做具体的布局操作,因此才会有LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager这样灵活的布局。而且我们可以通过实现onLayoutChildren,自定义LayoutManager。
  2. RecyclerView的布局过程中包含很多动画相关的处理。
  3. RecyclerView的数据改变的动画是在布局过程的第三步中统一触发的。并不是一调用notify之后立即触发。

2.3.OnDraw()

@Override
    public void onDraw(Canvas c) {
        super.onDraw(c);

        final int count = mItemDecorations.size();
        for (int i = 0; i < count; i++) {
            mItemDecorations.get(i).onDraw(c, this, mState);
        }
    }

除了绘制自己以外,还多调了一个mItemDecorations 的 onDraw() 方法.我们知道onDraw调用之前,会先调用draw方法,RecyclerView重写了draw方法:也就是先用draw()绘制item,然后再调用mItemDecorations的onDraw()来加一层图片,还要调用getItemOffsets(),让item的view发生一定的偏移,让在ItemView下的图片可以显示出来
参考文章

3.缓存机制

共有四级缓存机制,分别是Scrap,cache,viewCacheExtension,RecyclerPool
Recycler类的字段

public final class Recycler {
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
private ArrayList<ViewHolder> mChangedScrap = null;

final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();

private final List<ViewHolder>
        mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);

private RecycledViewPool mRecyclerPool;

private ViewCacheExtension mViewCacheExtension;

屏幕内缓存 :指在屏幕中显示的ViewHolder,这些ViewHolder会缓存在mAttachedScrap、mChangedScrap中 。(2个scrap字段中)
mChangedScrap表示数据已经改变的ViewHolder列表
mAttachedScrap未与RecyclerView分离的ViewHolder列表
/*************************************
在这里插入图片描述

RecycledViewPool是最底层的缓存机制,当Cache缓存满了以后会根据FIFO(先进先出)的规则把Cache最早的ViewHolder移到RecyclerViewPool.
默认的缓存数量是5个.特点是,从Cache里面移出的ViewHolder再存入RecycledViewPool之前ViewHolder的数据会被全部重置,相当于一个新的ViewHolder。所有从RecycledViewPool取出来的时候需要回调onBindViewHolder ()方法。
mRecyclerPool会根据ViewType把ViewHolder分别存储在不同的集合中,每个集合最多缓存5个ViewHolder。

在这里插入图片描述

优点

是1.mCacheViews的使用,可以做到屏幕外的列表项ItemView进入屏幕内时也无须bindView快速重用;2. b.mRecyclerPool可以供多个RecyclerView共同使用

RecyclerView最多可以缓存N(屏幕最多可显示的item数)+ 2 (屏幕外的缓存) + 5*M (M代表M个ViewType,缓存池的缓存)。
在这里插入图片描述

4.缓存策略

Recyclerview在获取ViewHolder时按四级缓存的顺序查找,如果没找到就创建。

5.Recycler的刷新机制(局部刷新)

这里使用的模式是观察者模式。
根据上述的关系图,与数据相关的都是适配器的工作。Adapter是数据源的直接接触者,当数据源发生变化时,它需要通知给RecyclerView。涉及到数据变化的检测,AdapterDataObsetvable, RecyclerViewDataObserver.
AdapterDataObservable是数据源变化时的被观察者。RecyclerViewDataObserver是观察者。
在这里插入图片描述

在开发中我们通常使用adapter.notifyXX()来刷新UI,实际上Adapter会调用AdapterDataObservable的notifyChanged():通知Observer数据发生变化
在这里插入图片描述
在这里插入图片描述

可以看出,数据集的变化最终会调用requestLayout() 对recyclerView重新进行绘制,onMeasure(),onLayout(), onDraw().主要是调用onLayoutChildren()函数,计算新的itemView的边界值
调用notifyxxxxx()时,会对屏幕内ItemView做预处理,修改ItemView相应的pos以及flag(流程图中红色部分)
调用fill()中RecyclerView.getViewForPosition(pos)时,RecyclerView通过对pos和flag的预处理,使得bindview只调用一次。

这个所有的子View是表示数据集里的所有data.

在这里插入图片描述
在这里插入图片描述

RecyclerView则是更加灵活地对每个View修改标志位,区分是否重新bindView

参考文档1
参考文档2

### Android RecyclerView 源码解析 #### 一、RecyclerView 的核心概念 `RecyclerView` 是 Android 中用于高效展示大量数据的组件之一。它通过视图复用机制减少内存消耗并提升性能。其主要组成部分包括 `ViewHolder`、`Adapter` 和 `LayoutManager`。 - **ViewHolder**: 负责保存视图中的子控件引用,避免重复查找操作。 - **Adapter**: 提供绑定数据到视图的功能。 - **LayoutManager**: 控制布局方式(线性、网格或其他自定义形式),管理视图的位置和回收逻辑[^1]。 --- #### 二、缓存与视图复用机制 `RecyclerView` 使用两种类型的缓存来优化性能: 1. **Scrap 缓存 (Active Views)** Scrap 缓存存储的是当前屏幕上不可见但仍可能被重新使用的视图。这些视图通常由 `Recycler` 管理,在滚动过程中会被快速重用。 2. **RecycledViewPool** 当某个视图完全离开屏幕范围时,会进入 `RecycledViewPool`。这是一个全局共享池,允许不同列表间复用相同的 ViewHolder 实例。例如,调用了如下代码: ```java getRecycledViewPool().putRecycledView(holder); ``` 这表示将指定的 `holder` 放入回收池中以便后续使用。 --- #### 三、removeAndRecycleView 方法分析 当需要从父容器中移除某项 View 并将其放入回收队列时,可以调用 `removeAndRecycleView` 方法。以下是其实现细节: ```java public void removeAndRecycleView(View child, Recycler recycler) { removeView(child); // 移除该 View recycler.recycleView(child); // 将 View 加入回收流程 } ``` 此方法的作用在于先将目标 View 从 UI 层面删除 (`removeView`),随后交由 `Recycler` 处理 (`recycleView`)。后者负责判断是否应立即将其销毁还是暂时保留于 Scrap 或 Pool 中待命[^2]。 --- #### 四、layoutChunk 方法剖析 `layoutChunk` 是 `RecyclerView` 布局过程的核心部分之一,主要用于逐个填充新项目至可见区域。它的典型调用场景如下所示: ```java layoutChunk(recycler, state, layoutState, layoutChunkResult); ``` 具体功能分解如下: - 参数说明: - `Recycler`: 提供可复用的视图实例集合。 - `state`: 表示当前状态对象,记录诸如剩余空间大小等信息。 - `layoutState`: 定义了如何获取下一个要显示的数据位置及其方向。 - `layoutChunkResult`: 存储本次执行的结果反馈。 - 执行流程概述:根据传入参数决定下一步渲染策略;如果存在可用资源,则尝试加载新的 Item 到界面上[^3]。 --- #### 五、总结 通过对上述几个关键函数的理解可以看出,`RecyclerView` 不仅实现了灵活多样的外观定制能力,还凭借高效的内部算法显著降低了运行开销。无论是局部更新还是整体刷新操作均能保持流畅体验。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值