转载自琼珶和予
RecyclerView 源码分析(一)
RecyclerView的三大流程
本系列文章楼主打算从几个地方说起。先是将RecyclerView当成一个普通的View,分别分析它的三大流程、事件传递(包括嵌套滑动)
;然后是分析RecyclerView的缓存原理
,这也是RecyclerView的精华所在;然后分析的是RecyclerView的Adapter、LayoutManager、ItemAnimator和ItemDecoration
。最后就是RecyclerView的扩展
,包括LayoutManager的自定义和使用RecyclerView常见的坑
等。
1. 概述
在分析RecyclerView源码之前,我们还是对RecyclerView有一个初步的了解,简单的了解它是什么,它的基本结构有哪些。
RecyclerView是Google爸爸在2014年的IO大会提出来。但是在实际开发中,自从有了RecyclerView,ListView和GridView就很少用了,所以我们暂且认为RecyclerView的目的是替代ListView和GridView。
RecyclerView本身是一个展示大量数据的控件,相比较ListView,RecyclerView的4级缓存(也有人说是3级缓存,这些都不重要)就表现的非常出色,在性能方面相比于ListView提升了不少。同时由于LayoutManager的存在,让RecyclerView不仅有ListView的特点,同时兼有GridView的特点。这可能是RecyclerView受欢迎的原因之一吧。
RecyclerView在设计方面上也是非常的灵活,不同的部分承担着不同的职责。其中Adapter负责提供数据,包括创建ViewHolder和绑定数据,LayoutManager负责ItemView的测量和布局,ItemAnimator负责每个ItemView的动画,ItemDecoration负责每个ItemView的间隙。这种插拔式的架构使得RecyclerView变得非常的灵活,每一个人都可以根据自身的需求来定义不同的部分。
正因为这种插拔式的设计,使得RecyclerView在使用上相比较于其他的控件稍微难那么一点点,不过这都不算事,谁叫RecyclerView这么惹人爱呢。
好了,好像废话有点多,现在我们正式来分析源码吧,本文的重点是RecyclerView的三大流程。
注意,本文RecyclerView源码均来自于27.1.1
2. measure
不管RecyclerView是多么神奇,它也是一个View,所以分析它的三大流程是非常有必要的。同时,如果了解过RecyclerView的同学应该都知道,RecyclerView的三大流程跟普通的View比较,有很大的不同。
首先,我们来看看measure过程,来看看RecyclerView的onMeasure方法。
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
// 第一种情况
}
if (mLayout.isAutoMeasureEnabled()) {
// 第二种情况
} else {
// 第三种情况
}
}
onMeasure方法还是有点长,这里我将它分为3种情况,我将简单解释这三种情况。
- mLayout即LayoutManager的对象。我们知道,当RecyclerView的LayoutManager为空时,RecyclerView不能显示任何的数据,在这里我们找到答案。
- LayoutManager开启了自动测量时,这是一种情况。在这种情况下,有可能会测量两次。
- 第三种情况就是没有开启自动测量的情况,这种情况比较少,因为为了RecyclerView支持warp_content属性,系统提供的LayoutManager都开启自动测量的,不过我们还是要分析的。
首先我们来第一种情况。
(1)当LayoutManager为空时
这种情况下比较简单,我们来看看源码:
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
return;
}
直接调了defaultOnMeasure方法,我们继续来看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方法来设置宽高。我们来看看:
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);
}
}
chooseSize方法表达的意思比较简单,就是通过RecyclerView的测量mode来获取不同的值,这里就不详细的解释了。
到此,第一种情况就分析完毕了。因为当LayoutManager为空时,那么当RecyclerView处于onLayout阶段时,会调用dispatchLayout方法。而在dispatchLayout方法里面有这么一行代码:
if (mLayout == null) {
Log.e(TAG, "No layout manager attached; skipping layout");
// leave the state in START
return;
}
所以,当LayoutManager为空时,不显示任何数据是理所当然的。
现在我们来看看第二种情况,也就是正常的情况。
(2)当LayoutManager开启了自动测量
在分析这种情况之前,我们先对了解几个东西。
RecyclerView的测量分为两步,分别调用dispatchLayoutStep1和dispatchLayoutStep2。同时,了解过RecyclerView源码的同学应该知道在RecyclerView的源码里面还一个dispatchLayoutStep3方法。这三个方法的方法名比较接近,所以容易让人搞混淆。本文会详细的讲解这三个方法的作用。
由于在这种情况下,只会调用dispatchLayoutStep1和dispatchLayoutStep2这两个方法,所以这里会重点的讲解这两个方法
。而dispatchLayoutStep3方法的调用在RecyclerView的onLayout方法里面,所以在后面分析onLayout方法时再来看dispatchLayoutStep3方法。
我们在分析之前,先来看一个东西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.
*/
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (measureSpecModeIsExactly || mAdapter == null) {
return;
}
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();