RecyclerView 全面的源码分析

本文全面分析了Android的RecyclerView,从 measure、layout、draw、touch 和 Adapter 数据更新五个维度深入探讨。重点讲解了 RecyclerView 的 measure 过程,layout 中的 dispatchLayoutStep 三个方法,特别是Recycler的缓存机制和动画处理。同时,介绍了ItemDecoration的绘制逻辑以及手势滑动的处理。文章最后讨论了Adapter与数据集的更新流程,揭示了RecyclerView如何高效地更新视图和播放相应动画。
摘要由CSDN通过智能技术生成

RecyclerView 概要

RecyclerView是Android 5.0开始提供一个可回收容器,它比 ListView更先进灵活更具扩展性,可高效重用和滚动,能方便定制各种布局和交互效果。它继承自 ViewGroup,那么它展示和交互也离不开 measure , layout , draw , touch ,adapter 五步。后面将以这五步来抽丝剥茧分析其工作原理,其中 LayoutManager 以最简单的 LinearLayoutManager为例。更多经典自定义 ViewGroup集合

RecyclerView 的辅助类非常多,相联紧密的类都作为其内部类存在,如下图:

recycler_class

在这个类之外还有比较重要的三个类:AdapterHelper,ChildHelper,ViewInfoStore 。AdapterHelper负责处理 Adapter 里的数据集发生变化时的预处理;ChildHelper负责管理和访问 RecyclerView 的子视图;ViewInfoStore 记录 pre-layout 和 post-layout 阶段的 ViewHolder 状态信息,方便分析差异做相应的动画。

RecyclerView 的使用

首先需要引入 recyclerview-v7 的包,在 build.gradle的 dependencies 块中添加

compile 'com.android.support:recyclerview-v7:25.2.0@aar'
或
compile(name: 'recyclerview-v7-25.2.0', ext: 'aar') //直接引用 aar 文件,需要flatDir中添加 aar 目录dirs

使用时自已的 YourAdapter 需要继承自 RecyclerView.Adapter,YourViewHolder 要继承RecyclerView.ViewHolder.
除此对 RecyclerView 还需要设置 LayoutManager,SDK提供了常用的三种布局管理器LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager。要设置divider或其它修饰可通过RecyclerView的addItemDecoration来定制。而事件可以在 Adapter 的onBindViewHolder里去绑定。总的来说 RecyclerView 的定制性是相当高的,用法就不多讲,下面重点讲解其工作原理。

RecyclerView(25.2.0) 原理分析(五个维度深入)。

1.measure 过程,因为RecyclerView 的默认三大布局管理器的 mAutoMeasure 默认都是true,所以measure过程实际被LayoutManager接管。关键源码如下:

protected void onMeasure(int widthSpec, int heightSpec) {
    ......
    if (mLayout.mAutoMeasure) {
        final int widthMode = MeasureSpec.getMode(widthSpec);
        final int heightMode = MeasureSpec.getMode(heightSpec);
        final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
                && heightMode == MeasureSpec.EXACTLY;
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        if (skipMeasure || mAdapter == null) {
            return;
        }
        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();
        }
        ......
        dispatchLayoutStep2();                      
        mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
        ......
    }else{
        ......
    }

首先调用了LayoutManager的onMeasure,内部只是调用了RecyclerView的defaultOnMeasure,这一步实质是在忽略子视图情况下测绘容器大小。当RecyclerView本身的宽和高都是精确值即没有wrap_content时skipMeasure为true 此时子视图的测绘将会延迟到 onLayout 过程,后续会讲到。skipMeasure 为 true 或 adapter 为空时都是没有必要测绘子视图来决定自身大小的。不确定自身大小(存在wrap_content)才会走后续的 dispatchLayoutStep1()和 dispatchLayoutStep2()方法。而测绘和布局子视图就是在dispatchLayoutStep2这一步完成的。当调用mLayout.setMeasuredDimensionFromChildren,内部会调用 RecyclerView.setMeasuredDimension方法从而定格了 RecyclerView 自身容器的大小。

注:dispatchLayoutStep2在onLayout 中也有调用稍后会讲到其具体实现

2.layout 过程调用了dispatchLayout,它会根据mState.mLayoutStep的值调用不同的dispatchLayout步骤,源码概要如下:

void dispatchLayout() {
    ......
    mState.mIsMeasuring = false;
    if (mState.mLayoutStep == State.STEP_START) {
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
            mLayout.getHeight() != getHeight()) {
        // First 2 steps are done in onMeasure but looks like we have to run again due to
        // changed size.
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    } else {
        // always make sure we sync them (to ensure mode is exact)
        mLayout.setExactMeasureSpecsFrom(this);
    }
    dispatchLayoutStep3();

可以发现 layout 过程也像 onMeasure 里有类似调用 即dispatchLayoutStep1和dispatchLayoutStep2. 这是因为RecyclerView对子视图的 measure 和 layout 都是通过dispatchLayoutStep2这个方法一起完成的。当然我们不必担心它会重复调用了,因为还有一个mState.mLayoutStep来控制测绘步骤呢。当值为STEP_START时会调用dispatchLayoutStep1之后赋值mLayoutStep = State.STEP_LAYOUT,当值为STEP_LAYOUT时才会调用dispatchLayoutStep2然后再赋值mLayoutStep = State.STEP_ANIMATIONS,而当值为STEP_ANIMATIONS时才会调用dispatchLayoutStep3然后重新赋值为STEP_START。确保 onMeasure 到 onLayout 中各步骤只会有一次。

3.重点分析dispatchLayoutStep三个方法和 Recycler 类。

每一次数据集变化或是调用了 requestLayout 都会走dispatchLayoutStep这几个方法。这个方法也是 RecyclerView 对子视图填充布局和做动画的核心所在。上面在 onMeasure 和 onLayout 时也多次出现,以下就讲综合讲述它们各阶段的作用 。
回顾 measure回顾 layout

其中最重要的是实现了子视图测量和填充的方法是dispatchLayoutStep2,它是,下面我们先讲
dispatchLayoutStep1和dispatchLayoutStep3,这两个方法都是与执行不同操作的动画紧密相关且相对简单些(动作如 Adapter 的 add、remove、insert 等)。

(1).dispatchLayoutStep1 布局预处理,step3 做动画的基础。

private void dispatchLayoutStep1() {
      ......
      mViewInfoStore.clear();
      if (mState.mRunSimpleAnimations) {
          // Step 0: Find out where all non-removed items are, pre-layout
          int count = mChildHelper.getChildCount();
          for (
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值