三大流程源码解析
RecyclerView是google提出ListView的替代品。更强大的缓存支持,Grid模式和Horizantal模式展示。
1. 基本使用方式:
RecyclerView.setLayoutManager()
RecyclerView.setAdapter()
RecyclerView.setItemAnimator()
RecyclerView.addItemDecoration()
解释说明。
- setLayoutManager:必选项,设置 RV 的布局管理器,决定 RV 的显示风格。常用的有线性布局管理器(LinearLayoutManager)、网格布局管理器(GridLayoutManager)、瀑布流布局管理器(StaggeredGridLayoutManager)。
- setAdapter:必选项,设置 RV 的数据适配器。当数据发生改变时,以通知者的身份,通知 RV 数据改变进行列表刷新操作。
- addItemDecoration:非必选项,设置 RV 中 Item 的装饰器,经常用来设置 Item 的分割线。
- setItemAnimator:非必选项,设置 RV 中 Item 的动画。
RecyclerView本身也是一个自定义View,自然离不开三大流程。下面咱们分别看下它的onMeasure、onLayout、onDraw方法。RecyclerView将每一个 ItemView 显示到屏幕上,在显示和滑动过程中,通过缓存复用来提升整体性能。
public class RecyclerView extends ViewGroup implements ScrollingView,
NestedScrollingChild2, NestedScrollingChild3 {
... ...
}
2. onMeasure方法
首先看下onMeasure方法
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
// 第一种情况,当LayoutManager为空时
}
if (mLayout.isAutoMeasureEnabled()) {
// 第二种情况,LayoutManager开启自动测量。
} else {
// 第三种情况,LayoutManager没有开启自动测量
}
}
方法比较长。这里我将它分为3种情况,我将简单解释这三种情况:
-
mLayout 即 LayoutManager 的对象。我们知道,当 RecyclerView 的 LayoutManager 为空时,RecyclerView 不能显示任何的数据,在这里我们找到答案。
-
LayoutManager 开启了自动测量时,这是一种情况。在这种情况下,有可能会测量两次。
-
第三种情况就是没有开启自动测量的情况,这种情况比较少,因为 RecyclerView 为了支持 warp_content 属性,系统提供的 LayoutManager 都开启自动测量的,不过还是要分析的
2.1 LayoutManager == null的情况
这种情况下比较简单,我们来看看源码:
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
return;
}
这里是调用了 defaultOnMeasure 方法,
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);
}
在 defaultOnMeasure 方法里面,主要是通过 LayoutManager 的 chooseSize 方法来计算宽高,最后调用 setMeasuredDimension 方法来设置宽高。下面来看下 chooseSize 的具体逻辑:
public static int chooseSize(int spec, int desired, int min) {
final int mode = View.MeasureSpec.getMode(spec);
final int size = View.MeasureSpec.getSize(spec);
switch (mode) {
case View.MeasureSpec.EXACTLY:
return size;
case View.MeasureSpec.AT_MOST:
return Math.min(size, Math.max(desired, min));
case View.MeasureSpec.UNSPECIFIED:
default:
return Math.max(desired, min);
}
}
这里主要是根据不同的设置,来返回最终的大小。这块逻辑不是很懂的读者可以阅读前面提到的文章,里面详细解读了。但是这里有个问题需要指出来的就是没有测量子 view 的大小,这也是白屏的原因。因为 RecyclerView 的绘制其实是委托给 LayoutManager 来管理呢,LayoutManager = null 的情况下测量子 view 没有任何的意义。
2.2 LayoutManager 开启了自动测量
在分析这种情况之前,我们先对了解下mState.mLayoutStep。这个变量有几个取值情况,我们分别来看看:
| 取值 | 含义 |
|---|---|
| State.STEP_START | mState.mLayoutStep 的默认值,这种情况下,表示 RecyclerView 还未经历 dispatchLayoutStep1,因为 dispatchLayoutStep1 调用之后mState.mLayoutStep 会变为 State.STEP_LAYOUT。 |
| State.STEP_LAYOUT | 当 mState.mLayoutStep 为 State.STEP_LAYOUT 时,表示此时处于 layout 阶段,这个阶段会调用 dispatchLayoutStep2 方法 layout RecyclerView 的children。调用 dispatchLayoutStep2 方法之后,此时 mState.mLayoutStep 变为了 State.STEP_ANIMATIONS。 |
| State.STEP_ANIMATIONS | 当 mState.mLayoutStep为 State.STEP_ANIMATIONS 时,表示 RecyclerView 处于第三个阶段,也就是执行动画的阶段,也就是调用 dispatchLayoutStep3方法。当 dispatchLayoutStep3 方法执行完毕之后,mState.mLayoutStep 又变为了 State.STEP_START。 |
从上表中,我们了解到 mState.mLayoutStep 的三个状态对应着不同的 dispatchLayoutStep 方法。
if (mLayout.isAutoMeasureEnabled()) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
/**
* This specific call should be considered deprecated and replaced with
* {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
* break existing third party code but all documentation directs developers to not
* override {@link LayoutManager#onMeasure(int, int)} when
* {@link LayoutManager#isAutoMeasureEnabled()} returns true.
*/
//1.mLayout.onMeasure方法
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (measureSpecModeIsExactly || mAdapter == null) {
return;
}
// 2.开始测量
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
}
// set dimensions in 2nd step. Pre-layout should happen with old dimensions for
// consistency
mLayout.setMeasureSpecs(widthSpec, heightSpec);
mState.mIsMeasuring = true;
// 3.第二次
dispatchLayoutStep2();
// now we can get the width and height from the children.
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
// if RecyclerView has non-exact width and height and if there is at least one child
// which also has non-exact width & height, we have to re-measure.
if (mLayout.shouldMeasureTwice()) {
mLayout.setMeasureSpecs(
MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
mState.mIsMeasuring = true;
dispatchLayoutStep2();
// now we can get the width and height from the children.
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
}
}
我们先来看下 LayoutManager#onMeasure() 方法。
public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
int heightSpec) {
mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
}
发现调用的 RecyclerView 的 defaultOnMeasure 方法,测量自己本身的大小。
dispatchLayoutStep1方法
咱们再接着往下走,看下dispatchLayoutStep1。这个方法和RV的动画息息相关。下面是 dispatchLayoutStep1() 方法的具体逻辑:
/**
* The first step of a layout where we;
* - process adapter updates
* - decide which animation should run
* - save information about current views
* - If necessary, run predictive layout and save its information
*/
private void dispatc

本文详细分析了RecyclerView的onMeasure、onLayout和onDraw方法,探讨了其内部如何处理LayoutManager的自动测量、数据更新、布局步骤及动画执行。着重解释了LayoutManager在不同情况下的测量逻辑,包括默认情况、开启自动测量和未开启自动测量的处理,以及onLayoutChildren方法中如何填充子视图。此外,还提到了ItemDecoration的onDraw方法在绘制过程中的作用。
最低0.47元/天 解锁文章
2350

被折叠的 条评论
为什么被折叠?



