RecyclerView 源码分析(一)RecyclerView的三大流程

本文详细分析了RecyclerView的三大核心流程:measure、layout和draw。首先介绍了RecyclerView的测量过程,包括当LayoutManager为空、开启自动测量和未开启自动测量时的不同处理。接着探讨了layout过程,强调了dispatchLayoutStep1、dispatchLayoutStep2和dispatchLayoutStep3的作用。最后,概述了draw阶段,包括调用super.draw方法、ItemDecoration的绘制和处理padding。文章还特别解析了LayoutManager的onLayoutChildren方法,详述了其如何确定锚点信息、回收ItemView并填充Children。
摘要由CSDN通过智能技术生成

转载自琼珶和予

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种情况,我将简单解释这三种情况。

  1. mLayout即LayoutManager的对象。我们知道,当RecyclerView的LayoutManager为空时,RecyclerView不能显示任何的数据,在这里我们找到答案。
  2. LayoutManager开启了自动测量时,这是一种情况。在这种情况下,有可能会测量两次。
  3. 第三种情况就是没有开启自动测量的情况,这种情况比较少,因为为了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的测量分为两步,分别调用dispatchLayoutStep1dispatchLayoutStep2。同时,了解过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();
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值