RecyclerView源码分析之一

写在前面:看RecyclerView源码有点"老虎吃天,无从下口"的感觉。多次提笔想写关于RecyclerView的源码相关文章,最终也是作罢。最大的原因还是感觉RecyclerView的源码太过复杂,怕自己不能胜任。也是看了一些网上的博客文章,有的文章看了也不止一遍。自己也就“照虎画猫”,来记录一下阅读源码的过程。

源码版本:androidx1.1.0

本文要旨:

  • 正常设置了LayoutManager和适配器以后,RecyclerView的measure、layout、draw流程。
  • 第一次是如何填充子View的。
  • 在滚动过程中(move和fling)的时候,是如何回收和填充子View的。我们这里不会看回收和填充子View的细节,只会看哪里发生了回收和填充子View调用操作。

我们就以RecyclerView最简单的使用方式为例进行分析。使用线性布局,方向为竖直方向,布局从上到下。

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
//先设置LayoutManager
recyclerView.setLayoutManager(linearLayoutManager);
//后设置适配器
recyclerView.setAdapter(...);

使用RecyclerView的两部曲:setLayoutManager()setAdapter

RecyclerView的setLayoutManager方法。

public void setLayoutManager(@Nullable LayoutManager layout) {
    if (layout == mLayout) {
        return;
    }
    //停止滚动
    stopScroll();
    
    //改变LayoutManager会做一些回收和清理工作
    if (mLayout != null) {
        // 停止所有的动画
        if (mItemAnimator != null) {
            mItemAnimator.endAnimations();
        }
        //回收Views和清理工作
        mLayout.removeAndRecycleAllViews(mRecycler);
        mLayout.removeAndRecycleScrapInt(mRecycler);
        mRecycler.clear();

        if (mIsAttached) {
            //从当前窗口分离
            mLayout.dispatchDetachedFromWindow(this, mRecycler);
        }
        mLayout.setRecyclerView(null);
        mLayout = null;
    } else {
        //清除缓存
        mRecycler.clear();
    }
    // this is just a defensive measure for faulty item animators.
    mChildHelper.removeAllViewsUnfiltered();
    mLayout = layout;
    if (layout != null) {
        //...
        //LayoutManager关联RecyclerView
        mLayout.setRecyclerView(this);
        if (mIsAttached) {
            mLayout.dispatchAttachedToWindow(this);
        }
    }
    mRecycler.updateViewCacheSize();
    //注释1处,请求measure、layout、draw。
    requestLayout();
}

注释1处,调用requestLayout方法请求measure、layout、draw。

RecyclerView的setAdapter方法。

public void setAdapter(@Nullable Adapter adapter) {
    // bail out if layout is frozen
    setLayoutFrozen(false);
    //注释1处
    setAdapterInternal(adapter, false, true);
    processDataSetCompletelyChanged(false);
    //注释2处,请求measure、layout、draw。
    requestLayout();
}

注释1处,调用setAdapterInternal方法。

private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
    if (mAdapter != null) {
        //取消注册老的适配器观察者
        mAdapter.unregisterAdapterDataObserver(mObserver);
        mAdapter.onDetachedFromRecyclerView(this);
    }
    if (!compatibleWithPrevious || removeAndRecycleViews) {
        removeAndRecycleViews();
    }
    mAdapterHelper.reset();
    final Adapter oldAdapter = mAdapter;
    mAdapter = adapter;
    if (adapter != null) {
        //注释1处,注册观察适配器,当适配器数据发生变化的时候,重新requestLayout
        adapter.registerAdapterDataObserver(mObserver);
        adapter.onAttachedToRecyclerView(this);
    }
    if (mLayout != null) {
        mLayout.onAdapterChanged(oldAdapter, mAdapter);
    }
    //mRecycler在适配器发生变化时,做一些清理操作
    mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    //标记RecyclerView发生了结构性变化
    mState.mStructureChanged = true;
}

注释1处,注册观察适配器,当适配器数据发生变化的时候,重新请求requestLayout

RecyclerView的setLayoutManager()setAdapter方法内部都会调用requestLayout方法,请求measure、layout、draw。接下来我们就看看这三大流程。

RecyclerView的onMeasure方法

@Override
protected void onMeasure(int widthSpec, int heightSpec) {
    if (mLayout == null) {
        //注释1处
        defaultOnMeasure(widthSpec, heightSpec);
        return;
    }
    //注释2处,是否开启自动测量,我们以LinearLayoutManager来分析,LinearLayoutManager默认是true。
    if (mLayout.isAutoMeasureEnabled()) {
        final int widthMode = MeasureSpec.getMode(widthSpec);
        final int heightMode = MeasureSpec.getMode(heightSpec);

        //注释3处,LayoutManager的onMeasure方法默认还是调用了RecyclerView的defaultOnMeasure方法
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        //注释4处,宽高的测量模式都是EXACTLY或者mAdapter为null直接return,就是使用defaultOnMeasure的测量结果
        final boolean measureSpecModeIsExactly =
                widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
        if (measureSpecModeIsExactly || mAdapter == null) {
            return;
        }
        //注释5处,当前布局阶段,默认是State.STEP_START
        if (mState.mLayoutStep == State.STEP_START) {
            //注释6处,
            dispatchLayoutStep1();
        }
        //为LayoutManager设置测量模式
        mLayout.setMeasureSpecs(widthSpec, heightSpec);
        //将RecyclerView的状态置为正在测量
        mState.mIsMeasuring = true;
        //注释7处,第二步布局
        dispatchLayoutStep2();

        //在dispatchLayoutStep2之后,重新设置LayoutManager的宽高信息
        mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

        // 如果RecyclerView没有准确的宽高信息并且RecyclerView至少有一个子View没有确定的宽高则重新测量。
        //这个步骤可以忽略。
        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);
        }
    } else {//自动测量为false,
        if (mHasFixedSize) {
            //如果有mHasFixedSize为true,mLayout直接测量返回
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            return;
        }
        // 自动测量为false并且mHasFixedSize为false的情况下,会执行自定义测量,执行一些动画等等。
        // custom onMeasure
        if (mAdapterUpdateDuringMeasure) {
            startInterceptRequestLayout();
            onEnterLayoutOrScroll();
            processAdapterUpdatesAndSetAnimationFlags();
            onExitLayoutOrScroll();
        
            if (mState.mRunPredictiveAnimations) {
                mState.mInPreLayout = true;
            } else {
                // consume remaining updates to provide a consistent state with the layout pass.
                mAdapterHelper.consumeUpdatesInOnePass();
                mState.mInPreLayout = false;
            }
            mAdapterUpdateDuringMeasure = false;
            stopInterceptRequestLayout(false);
        } else if (mState.mRunPredictiveAnimations) {
            // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
            // this means there is already an onMeasure() call performed to handle the pending
            // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
            // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
            // because getViewForPosition() will crash when LM uses a child to measure.
            setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
            return;
        }
        
        if (mAdapter != null) {
            mState.mItemCount = mAdapter.getItemCount();
        } else {
            mState.mItemCount = 0;
        }
        startInterceptRequestLayout();
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        stopInterceptRequestLayout(false);
        mState.mInPreLayout = false; // clear
    }
}

注释1处,如果没有设置LayoutManager,就调用defaultOnMeasure,然后直接return。

void defaultOnMeasure(int widthSpec, int heightSpec) {
    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);
}

就是根据测量模式获取RecyclerView的宽高信息,然后保存一下。

注释2处,是否开启自动测量,我们以LinearLayoutManager来分析,LinearLayoutManager默认开启了自动测量。

注释3处,LayoutManager的onMeasure方法默认还是调用了RecyclerView的defaultOnMeasure方法。

注释4处,宽高的测量模式都是EXACTLY或者mAdapter为null直接return,onMeasure流程结束,就是使用defaultOnMeasure的测量结果。

注释5处,当前布局阶段,mState默认是State.STEP_START,条件满足,会调用注释6处。这里提一下,mState是一个RecyclerView.State对象,用来保存RecyclerView有用的信息。并且,RecyclerView和各个组件之间(比如Recycler)通过传递mState来进行通信。

RecyclerView的dispatchLayoutStep1方法

/**
 * layout的第一步,在这个步骤会执行以下操作;
 * - 处理适配器更新
 * - 决定要运行哪种动画
 * - 保存当前views的信息
 * - 如果必要的话,运行预布局(layout)并保存相应的信息
 */
private void dispatchLayoutStep1() {
    mState.assertLayoutStep(State.STEP_START);
    fillRemainingScrollValues(mState);
    mState.mIsMeasuring = false;
    //开始拦截requestLayout的请求,避免多次响应requestLayout的调用,造成多次布局
    startInterceptRequestLayout();
    //mViewInfoStore中存储的是要执行动画的Views的相关信息,这里清除
    mViewInfoStore.clear();
    onEnterLayoutOrScroll();
    //注释1处,处理适配器更新和设置动画的标志位。
    processAdapterUpdatesAndSetAnimationFlags();
    saveFocusInfo();
    mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
    mItemsAddedOrRemoved = mItemsChanged = false;
    mState.mInPreLayout = mState.mRunPredictiveAnimations;
    //这里保存了适配器中数据的数量
    mState.mItemCount = mAdapter.getItemCount();
    findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);

    if (mState.mRunSimpleAnimations) {
        //第一次条件不满足...
    }
    if (mState.mRunPredictiveAnimations) {
        //第一次条件不满足
    } else {
        clearOldPositions();
    }
    onExitLayoutOrScroll();
    //停止拦截requestLayout的请求,
    stopInterceptRequestLayout(false);
    //将当前布局步骤赋值为State.STEP_LAYOUT
    mState.mLayoutStep = State.STEP_LAYOUT;
}

注释1处,处理适配器更新和设置动画的标志位。

private void processAdapterUpdatesAndSetAnimationFlags() {
    //第一次设置适配器的时候mDataSetHasChangedAfterLayout为true,mDispatchItemsChangedEvent为false
    if (mDataSetHasChangedAfterLayout) {
        // Processing these items have no value since data set changed unexpectedly.
        // Instead, we just reset it.
        mAdapterHelper.reset();
        if (mDispatchItemsChangedEvent) {
            mLayout.onItemsChanged(this);
        }
    }
    // simple animations are a subset of advanced animations (which will cause a
    // pre-layout step)
    // If layout supports predictive animations, pre-process to decide if we want to run them
    //注释1处,对于LinearLayoutManager来说条件满足,调用预处理方法,但是此时mPendingUpdates是empty的,没有什么可处理的
    if (predictiveItemAnimationsEnabled()) {
        mAdapterHelper.preProcess();
    } else {
        mAdapterHelper.consumeUpdatesInOnePass();
    }
    //第一次设置适配器,animationTypeSupported为false
    boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
    //第一次设置适配器mState.mRunSimpleAnimations为false
    mState.mRunSimpleAnimations = mFirstLayoutComplete
            && mItemAnimator != null
            && (mDataSetHasChangedAfterLayout
            || animationTypeSupported
            || mLayout.mRequestedSimpleAnimations)
            && (!mDataSetHasChangedAfterLayout
            || mAdapter.hasStableIds());
    //第一次设置适配器mState.mRunPredictiveAnimations为false
    mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
            && animationTypeSupported
            && !mDataSetHasChangedAfterLayout
            && predictiveItemAnimationsEnabled();
}

这里注意一下:第一次设置适配器mState.mRunSimpleAnimations为false,mState.mRunPredictiveAnimations为false,后面有很多地方会判断这两个变量。

第一次调用dispatchLayoutStep1的时候,此时RecyclerView还没有子View所以不会有什么动画执行。方法最后将mState.mLayoutStep置为了State.STEP_LAYOUT

RecyclerView的onMeasure方法的注释7处,调用dispatchLayoutStep2方法进行第二步布局。

/**
 * layout的第二步,在这里我们为最终状态执行View的真正的布局操作。如果必要的话,这个步骤可能会执行多次。
 */
private void dispatchLayoutStep2() {
    startInterceptRequestLayout();
    onEnterLayoutOrScroll();
    mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
    //消耗所有的延迟更新,这里以后再看
    mAdapterHelper.consumeUpdatesInOnePass();
    //获取适配器中数据的数量
    mState.mItemCount = mAdapter.getItemCount();
    mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;

    // Step 2: Run layout
    mState.mInPreLayout = false;
    //注释1处,布局子View
    mLayout.onLayoutChildren(mRecycler, mState);

    mState.mStructureChanged = false;
    mPendingSavedState = null;

    //为false
    mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
    //这里将状态置为了State.STEP_ANIMATIONS
    mState.mLayoutStep = State.STEP_ANIMATIONS;
    onExitLayoutOrScroll();
    stopInterceptRequestLayout(false);
}

注释1处,调用LayoutManager的onLayoutChildren方法。我们直接看LinearLayoutManager的onLayoutChildren方法。

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
    // layout的算法:
    // 1) 通过检查children和其他变量,找到一个锚点坐标和锚点的位置(锚点View对应在adapter中数据对应的位置)。 
    // 2) 从锚点向上填充RecyclerView。
    // 3) 从锚点向下填充RecyclerView。
    // 4) 滚动RecyclerView,做一些显示上的调整。
    if (mPendingSavedState != null || mPendingScrollPosition != RecyclerView.NO_POSITION) {
        if (state.getItemCount() == 0) {
            removeAndRecycleAllViews(recycler);
            return;
        }
    }
    if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
        mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
    }
    //如果mLayoutState为null的话,则创建。
    ensureLayoutState();
    mLayoutState.mRecycle = false;
    //决定布局顺序,是否要倒着布局。LinearLayoutManager默认是从上到下布局。
    resolveShouldLayoutReverse();

    final View focused = getFocusedChild();
    //注释1处,寻找锚点,为了简单,我们只看注释2处查找锚点的逻辑。
    if (!mAnchorInfo.mValid || mPendingScrollPosition != RecyclerView.NO_POSITION
            || mPendingSavedState != null) {
        mAnchorInfo.reset();
        //默认是false
        mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
        //注释2处,计算锚点位置和坐标
        updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
        mAnchorInfo.mValid = true;
    } else if (focused != null && (mOrientationHelper.getDecoratedStart(focused)
                    >= mOrientationHelper.getEndAfterPadding()
            || mOrientationHelper.getDecoratedEnd(focused)
            <= mOrientationHelper.getStartAfterPadding())) {
        //以获取焦点的子View为锚点
        mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));
    }
        

    //...
    //这里做了精简,extraForStart和extraForEnd我们都认为是0
    int extraForStart = 0;
    int extraForEnd = 0;
    //...
    int startOffset;
    int endOffset;
    final int firstLayoutDirection;
    if (mAnchorInfo.mLayoutFromEnd) {
        firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
                : LayoutState.ITEM_DIRECTION_HEAD;
    } else {
        //正常情况下,firstLayoutDirection为LayoutState.ITEM_DIRECTION_TAIL
        firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
                : LayoutState.ITEM_DIRECTION_TAIL;
    }

    onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection);
    //注释3处,如果当前存在attach到RecyclerView的View,则临时detach,后面再复用。
    detachAndScrapAttachedViews(recycler);
    mLayoutState.mInfinite = resolveIsInfinite();
    mLayoutState.mIsPreLayout = state.isPreLayout();
    // noRecycleSpace not needed: recycling doesn't happen in below's fill
    // invocations because mScrollingOffset is set to SCROLLING_OFFSET_NaN
    mLayoutState.mNoRecycleSpace = 0;
    if (mAnchorInfo.mLayoutFromEnd) {//正常情况为该条件不满足。我们分析else的情况。
        //...
    } else {
        //注释4处,向end方向填充的时候,先计算一些信息。
        updateLayoutStateToFillEnd(mAnchorInfo);
        mLayoutState.mExtraFillSpace = extraForEnd;
        //注释5处,从锚点开始向end方向填充
        fill(recycler, mLayoutState, state, false);
        endOffset = mLayoutState.mOffset;
        final int lastElement = mLayoutState.mCurrentPosition;
        if (mLayoutState.mAvailable > 0) {
            extraForStart += mLayoutState.mAvailable;
        }
        //注释6处,向start方向填充的时候,计算一些信息。
        updateLayoutStateToFillStart(mAnchorInfo);
        mLayoutState.mExtraFillSpace = extraForStart;
        mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
        //注释7处,填充
        fill(recycler, mLayoutState, state, false);
        startOffset = mLayoutState.mOffset;

        if (mLayoutState.mAvailable > 0) {//什么时候会满足这个条件呢?暂时不清楚
            extraForEnd = mLayoutState.mAvailable;
            // start could not consume all it should. add more items towards end
            updateLayoutStateToFillEnd(lastElement, endOffset);
            mLayoutState.mExtraFillSpace = extraForEnd;
            fill(recycler, mLayoutState, state, false);
            endOffset = mLayoutState.mOffset;
        }
    }

    // changes may cause gaps on the UI, try to fix them.
    // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have
    // changed
    if (getChildCount() > 0) {
        // because layout from end may be changed by scroll to position
        // we re-calculate it.
        // find which side we should check for gaps.
        if (mShouldReverseLayout ^ mStackFromEnd) {//默认情况下为false,我们看else分支
            //...
        } else {
            //注释8处,
            int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);
            startOffset += fixOffset;
            //返回的fixOffset为负数,可能会导致最后一个子View的bottom坐标,小于RecyclerView的paddintBottom,所以如果有更多的数据的话,应该继续向下填充View。
            endOffset += fixOffset;
            //注释9处,
            fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);
            startOffset += fixOffset;
            endOffset += fixOffset;
        }
    }
    //如果必要的话,为预执行动画布局子View。
    layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
    if (!state.isPreLayout()) {
        //注释10处,如果不是处于预布局状态,标记布局结束。
        mOrientationHelper.onLayoutComplete();
    } else {
        //注释11处,否则重置mAnchorInfo
        mAnchorInfo.reset();
    }
    mLastStackFromEnd = mStackFromEnd;
    
}

注释1处,找到锚点,为了简单,我们只看注释2处查找锚点的逻辑。

注释2处,计算锚点位置和坐标。我们直接看LinearLayoutManager的updateAnchorInfoForLayout方法。

private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state,
            AnchorInfo anchorInfo) {
    if (updateAnchorFromPendingData(state, anchorInfo)) {
        if (DEBUG) {
            Log.d(TAG, "updated anchor info from pending information");
        }
        return;
    }
    //第一个布局的时候,还没有子View
    if (updateAnchorFromChildren(recycler, state, anchorInfo)) {
        if (DEBUG) {
            Log.d(TAG, "updated anchor info from existing children");
        }
        return;
    }
    if (DEBUG) {
        Log.d(TAG, "deciding anchor info for fresh state");
    }
    //为anchorInfo的mCoordinate赋值。最终获取的如果是竖直方向,就是RecyclerView的topPadding,横向就是Recycleriew的leftPadding
    anchorInfo.assignCoordinateFromPadding();
    //注释1处
    anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;
}

第一次布局的时候会走到注释1处,如果mStackFromEnd为false,锚点anchorInfo.mPosition就是0。

注释3处,如果当前存在attach到RecyclerView的View,则临时detach,后面再复用。

注释4处,向end方向填充的时候,先更新布局状态,计算一些信息。mLayoutState是一个LinearLayoutManager.LayoutState对象,用来在布局过程中保存一些信息。在布局结束后不会再保存信息。

private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) {
    updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate);
}

private void updateLayoutStateToFillEnd(int itemPosition, int offset) {
    //需要填充的像素数,以竖直方向来说就是RecyclerView的高度减去PaddingTop减去paddingBottom
    mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset;
    //向尾部填充
    mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
            LayoutState.ITEM_DIRECTION_TAIL;
    //从前面的分析我们知道,默认itemPosition是0
    mLayoutState.mCurrentPosition = itemPosition;
    //默认向尾部(竖直方向,就是向下布局)
    mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END;
    mLayoutState.mOffset = offset;
    //mLayoutState将mScrollingOffset置为LayoutState.SCROLLING_OFFSET_NaN
    mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
}

LayoutManager的onLayoutChildren方法的注释5处,调用fill方法开始从锚点开始向end方向(对于默认的LinearLayoutManager,就是从锚点向下)填充RecyclerView,传入的最后一个参数为false注意一下。

fill(recycler, mLayoutState, state, false);

LinearLayoutManager的fill方法。

/** 神奇的方法:
 * @param recycler        当前关联到RecyclerView的recycler。
 * @param layoutState     该如何填充可用空间的配置信息。
 * @param state           Context passed by the RecyclerView to control scroll steps.
 * @param stopOnFocusable 如果为true的话,遇到第一个可获取焦点的View则停止填充。
 * @return 返回添加的像素。
 */
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    // max offset we should set is mFastScroll + available
    final int start = layoutState.mAvailable;
    //...
    //注释0处,如果layoutState.mScrollingOffset不为SCROLLING_OFFSET_NaN的话,调用recycleByLayoutState方法,从这个方法名,我们可以看出来,这是一个回收View的方法。
    if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
        //...
        recycleByLayoutState(recycler, layoutState);
    }
    //可以布局的空间
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        layoutChunkResult.resetInternal();
        //注释1处,获取并添加子View,然后测量、布局子View并将分割线考虑在内。
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        //条件满足的话,跳出循环
        if (layoutChunkResult.mFinished) {
            break;
        }
        //累加layoutState的偏移量
        layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
        //条件满足的时候,减去已经消耗的空间
        if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null
                            || !state.isPreLayout()) {
            layoutState.mAvailable -= layoutChunkResult.mConsumed;
            // we keep a separate remaining space because mAvailable is important for recycling
            remainingSpace -= layoutChunkResult.mConsumed;
        }
        //注释2处,这里也会判断是否要调用recycleByLayoutState方法。
        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }
                recycleByLayoutState(recycler, layoutState);
            }
        if (stopOnFocusable && layoutChunkResult.mFocusable) {
            break;
        }
    }
    
    return start - layoutState.mAvailable;
}

注释1处,LinearLayoutManager的layoutChunk方法。

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    //注释1处,获取子View,可能是从缓存中或者新创建的View。后面分析缓存相关的点的时候再看。
    View view = layoutState.next(recycler);
    if (view == null) {
        //注释2处,如果获取到的子View为null,将LayoutChunkResult的mFinished置为true,用于跳出循环然后直接return。
        result.mFinished = true;
        return;
    }
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    if (layoutState.mScrapList == null) {
        if (mShouldReverseLayout == (layoutState.mLayoutDirection
                == LayoutState.LAYOUT_START)) {
            //注释3处
            addView(view);  
        } else {
            //注释4处
            addView(view, 0);
        }
    } else {
        if (mShouldReverseLayout == (layoutState.mLayoutDirection
                == LayoutState.LAYOUT_START)) {
            addDisappearingView(view);
        } else {
            addDisappearingView(view, 0);
        }
    }
    //注释5处,测量子View的大小
    measureChildWithMargins(view, 0, 0);
    result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
    int left, top, right, bottom;
    if (mOrientation == VERTICAL) {
        if (isLayoutRTL()) {
            right = getWidth() - getPaddingRight();
            left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
        } else {
            left = getPaddingLeft();
            right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
        }
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            bottom = layoutState.mOffset;
            top = layoutState.mOffset - result.mConsumed;
        } else {
            top = layoutState.mOffset;
            bottom = layoutState.mOffset + result.mConsumed;
        }
    } else {
        top = getPaddingTop();
        bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);

        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            right = layoutState.mOffset;
            left = layoutState.mOffset - result.mConsumed;
        } else {
            left = layoutState.mOffset;
            right = layoutState.mOffset + result.mConsumed;
        }
    }
    //注释6处,布局子View,并将margin和分割线也考虑在内。
    layoutDecoratedWithMargins(view, left, top, right, bottom);
    // Consume the available space if the view is not removed OR changed
    if (params.isItemRemoved() || params.isItemChanged()) {
        result.mIgnoreConsumed = true;
    }
    result.mFocusable = view.hasFocusable();
}

注释1处,获取子View,可能是从缓存中或者新创建的View。可以说这个就是复用View的入口。后面分析缓存相关的点的时候再看。

注释2处,如果获取到的子View为null,将LayoutChunkResult的mFinished置为true,用于跳出循环然后直接return。

注释3处和注释4处根据填充方向添加子View。这里有点没看懂,总感觉方向反了呀。如果向尾部填充的时候,会走到注释4处的代码addView(view, 0),每次都把创建的View添加到0的位置,那不就反了吗?

这里注意一下,经过研究发现如果是通过createViewHolder创建的ViewHolder返回的View,最终调用ViewGroup的addView方法的时候,传入的index为-1,然后View就是添加到ViewGroup的末尾。具体代码细节就不贴了。

注释5处,测量子View的大小。

注释6处,布局子View,并将margin和分割线也考虑在内。

我们回到LayoutManager的onLayoutChildren方法的注释6处,向start方向填充的时候,计算一些信息,逻辑和updateLayoutStateToFillEnd类似,不再赘述。

LayoutManager的onLayoutChildren方法的注释7处,调用fill方法继续填充。这里注意一下,如果我们开始找到的锚点坐标是第0个位置, 那么向上填充的空间是0,也就是说向上填充其实没有真正添加子View。

注释8处,做一些滚动调整。什么意思呢?注意传入的canOffsetChildren参数为true。

做一些滚动调整。什么意思呢?就是说:我们从锚点开始向上填充子View的时候,如果锚点不是从index为0开始的,当向上填充的时候,可能会出现:

  1. 最上面一个子View的top坐标大于RecyclerView的paddingTop坐标,比如RecyclerView的paddingTop坐标是10 ,最上面一个子View的top坐标是20。那么就有10像素的空白没有填充,这个时候我们就应该将RecyclerView向上滚动10像素,然后从RecyclerView的最后一个子View再继续向下填充子View。
private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler,
        RecyclerView.State state, boolean canOffsetChildren) {
    int gap = startOffset - mOrientationHelper.getStartAfterPadding();
    int fixOffset = 0;
    if (gap > 0) {
        //注释1处,check if we should fix this gap.
        fixOffset = -scrollBy(gap, recycler, state);
    } else {
        return 0; // nothing to fix
    }
    //改变startOffset的值
    startOffset += fixOffset;
    if (canOffsetChildren) {
        //再次计算是否有空白,如果gap大于0,偏移所有的子View。
        gap = startOffset - mOrientationHelper.getStartAfterPadding();
        if (gap > 0) {
            mOrientationHelper.offsetChildren(-gap);
            return fixOffset - gap;
        }
    }
    return fixOffset;
}

注释1处,gap大于0,调用LinearLayoutManager的scrollBy方法。

int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (getChildCount() == 0 || delta == 0) {
        return 0;
    }
    ensureLayoutState();
    mLayoutState.mRecycle = true;
    //delta > 0,向下填充,layoutDirection为LayoutState.LAYOUT_END
    final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
    final int absDelta = Math.abs(delta);
    //注释0处,
    updateLayoutState(layoutDirection, absDelta, true, state);
    //注释1处,注意,这里继续调用fill方法填充子View。
    final int consumed = mLayoutState.mScrollingOffset
            + fill(recycler, mLayoutState, state, false);
    if (consumed < 0) {
        if (DEBUG) {
            Log.d(TAG, "Don't have any more elements to scroll");
        }
        return 0;
    }
    final int scrolled = absDelta > consumed ? layoutDirection * consumed : delta;
    //注释2处,偏移所有的子View
    mOrientationHelper.offsetChildren(-scrolled);
    if (DEBUG) {
        Log.d(TAG, "scroll req: " + delta + " scrolled: " + scrolled);
    }
    mLayoutState.mLastScrollDelta = scrolled;
    return scrolled;
}

注意:注释0处,调用了updateLayoutState方法,内部将mLayoutState.mScrollingOffset设置为一个不等于LayoutState.SCROLLING_OFFSET_NaN的值。在后面的fill方法里面,会判断mLayoutState.mScrollingOffset不等于LayoutState.SCROLLING_OFFSET_NaN的话,会进行View的回收操作。我们后面再分析滚动的时候再看一下。

private void updateLayoutState(int layoutDirection, int requiredSpace,
            boolean canUseExistingSpace, RecyclerView.State state) {
    // If parent provides a hint, don't measure unlimited.
    mLayoutState.mInfinite = resolveIsInfinite();
    //layoutDirection为LayoutState.LAYOUT_END
    mLayoutState.mLayoutDirection = layoutDirection;
    mReusableIntPair[0] = 0;
    mReusableIntPair[1] = 0;
    calculateExtraLayoutSpace(state, mReusableIntPair);
    int extraForStart = Math.max(0, mReusableIntPair[0]);
    int extraForEnd = Math.max(0, mReusableIntPair[1]);
    //是否向下填充
    boolean layoutToEnd = layoutDirection == LayoutState.LAYOUT_END;
    mLayoutState.mExtraFillSpace = layoutToEnd ? extraForEnd : extraForStart;
    mLayoutState.mNoRecycleSpace = layoutToEnd ? extraForStart : extraForEnd;
    int scrollingOffset;
    if (layoutToEnd) {//向尾部填充
        mLayoutState.mExtraFillSpace += mOrientationHelper.getEndPadding();
        // 获取最接近尾部的child
        final View child = getChildClosestToEnd();
        // 默认是LayoutState.ITEM_DIRECTION_TAIL
        mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
                : LayoutState.ITEM_DIRECTION_TAIL;
        //mCurrentPosition在
        mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
        mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
            // 需要滑动的空间,竖直方向来说,最后一个子View的bottom坐标减去RecyclerView的paddingBottom坐标的差值。
            //我认为在正常情况下,这是一个大于等于0的数值。
            scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
                    - mOrientationHelper.getEndAfterPadding();

        } else {
            final View child = getChildClosestToStart();
            mLayoutState.mExtraFillSpace += mOrientationHelper.getStartAfterPadding();
            mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
                    : LayoutState.ITEM_DIRECTION_HEAD;
            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
            mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
            //需要滑动的空间,竖直方向来说,RecyclerView的paddingTop坐标减去第一个子View的top坐标的差值。
            scrollingOffset = -mOrientationHelper.getDecoratedStart(child)
                    + mOrientationHelper.getStartAfterPadding();
        }
        mLayoutState.mAvailable = requiredSpace;
        if (canUseExistingSpace) {
            //可用的填充空间减去要滚动的距离,赋值以后感觉mLayoutState.mAvailable是一个小于等于0的数值。
            mLayoutState.mAvailable -= scrollingOffset;
        }
        mLayoutState.mScrollingOffset = scrollingOffset;
    }

注释1处,注意,这里继续调用fill方法填充子View。感觉此时mLayoutState.mAvailable是一个小于等于0的数值,并不会真正的填充子View。而是在下面的逻辑中进行滚动,偏移所有的子View。

注释2处,偏移所有的子View,保证RecyclerView的第一个子View的top坐标就是RecyclerView的top坐标。

LayoutManager的onLayoutChildren方法的注释9处,调用fixLayoutEndGap方法,和fixLayoutStartGap类似的逻辑。但是注意这里传入的canOffsetChildren位false。

private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler,
        RecyclerView.State state, boolean canOffsetChildren) {
    //这个值可能大于0
    int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
    int fixOffset = 0;
    if (gap > 0) {
        //注释1处,这个时候内部会向上填充,我感觉经过fixLayoutStartGap以后,这里是不会再向上填充的。
        fixOffset = -scrollBy(-gap, recycler, state);
    } else {
        return 0; // nothing to fix
    }
        // move offset according to scroll amount
        endOffset += fixOffset;
        if (canOffsetChildren) {//这里传入的是false是不会偏移View的
            // re-calculate gap, see if we could fix it
            gap = mOrientationHelper.getEndAfterPadding() - endOffset;
            if (gap > 0) {
                mOrientationHelper.offsetChildren(gap);
                return gap + fixOffset;
            }
        }
        return fixOffset;
    }

LayoutManager的onLayoutChildren方法的注释10处,如果不是处于预布局阶段,标记布局结束,不用运行动画。

LayoutManager的onLayoutChildren方法的注释11处,重置mAnchorInfo,后面需要运行动画。

到这里,dispatchLayoutStep2算是分析完了,onMeasure方法的分析也到此为止。其实我们发现在onMeasure的过程中已经做了一些布局子View的事情。(这也算是分析,不就是把源码贴出来,加了点注释吗?这。。。)

接下来我们看看RecyclerView的onLayout方法

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
    //注释1处,调用dispatchLayout方法。
    dispatchLayout();
    TraceCompat.endSection();
    //注释2处。标记第一次布局完成。
    mFirstLayoutComplete = true;
}

注释2处,标记第一次布局完成。

注释1处,调用dispatchLayout方法。

/**
 * 该方法可以看做是layoutChildren()方法的一个包装,处理由于布局造成的动画改变。动画的工作机制基于有5中不同类型的动画的假设:
 * PERSISTENT: 在布局前后,items一直可见。
 * REMOVED: 在布局之前items可见,在布局之后,items被应用移除。
 * ADDED: 在布局之前items不存在,items是应用添加到RecyclerView的。
 * DISAPPEARING: 在布局前后items存在于数据集中,但是在布局过程中可见性由可见变为不可见。(这些items是由于其他变化的副作用而被移动到屏幕之外了)
 * APPEARING: 在布局前后items存在于数据集中,但是在布局过程中可见性由不可见变为可见。(这些items是由于其他变化的副作用而被移动到屏幕之中了)
 *
 * 方法的大体逻辑就是计算每个item在布局前后是否存在,并推断出它们处于上述五种状态的哪一种,然后设置不同的动画。
 * PERSISTENT类型的Views会运行animatePersistence动画
 * DISAPPEARING类型的Views运行animateDisappearance动画。
 * APPEARING类型的Views运行animateAppearance动画 
 * REMOVED和ADDED类型的Views执行animateChange动画。 
 */
void dispatchLayout() {
    if (mAdapter == null) {
        Log.e(TAG, "No adapter attached; skipping layout");
        // leave the state in START
        return;
    }
    if (mLayout == null) {
        Log.e(TAG, "No layout manager attached; skipping layout");
        // leave the state in START
        return;
    }
    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 {
        //正常情况下,会走到这里,将RecyclerView的测量信息同步到LayoutManager
        mLayout.setExactMeasureSpecsFrom(this);
    }
    //调用dispatchLayoutStep3方法。
    dispatchLayoutStep3();
}

RecyclerView的dispatchLayoutStep3方法。

/**
 * 布局的最后一步,在这里我们会保存和动画相关的Views的信息,触发动画并执行必要的清理工作。
 */
private void dispatchLayoutStep3() {
    mState.assertLayoutStep(State.STEP_ANIMATIONS);
    startInterceptRequestLayout();
    onEnterLayoutOrScroll();
    //将状态重置为State.STEP_START
    mState.mLayoutStep = State.STEP_START;
    //是否执行动画
    if (mState.mRunSimpleAnimations) {
        // Step 3: Find out where things are now, and process change animations.
        // traverse list in reverse because we may call animateChange in the loop which may
        // remove the target view holder.
        for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
            ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
            if (holder.shouldIgnore()) {
                continue;
            }
            long key = getChangedHolderKey(holder);
            final ItemHolderInfo animationInfo = mItemAnimator
                    .recordPostLayoutInformation(mState, holder);
            ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
            if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
                // 运行一个change动画

                // If an Item is CHANGED but the updated version is disappearing, it creates
                // a conflicting case.
                // Since a view that is marked as disappearing is likely to be going out of
                // bounds, we run a change animation. Both views will be cleaned automatically
                // once their animations finish.
                // On the other hand, if it is the same view holder instance, we run a
                // disappearing animation instead because we are not going to rebind the updated
                // VH unless it is enforced by the layout manager.
                final boolean oldDisappearing = mViewInfoStore.isDisappearing(
                        oldChangeViewHolder);
                final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
                if (oldDisappearing && oldChangeViewHolder == holder) {
                    // run disappear animation instead of change
                    mViewInfoStore.addToPostLayout(holder, animationInfo);
                } else {
                    final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
                            oldChangeViewHolder);
                    // we add and remove so that any post info is merged.
                    mViewInfoStore.addToPostLayout(holder, animationInfo);
                    ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
                    if (preInfo == null) {
                        handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
                    } else {
                        //运行change动画
                        animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
                                oldDisappearing, newDisappearing);
                    }
                }
            } else {
                //保存动画信息
                mViewInfoStore.addToPostLayout(holder, animationInfo);
            }
        }

        //处理view info lists 并触发动画
        mViewInfoStore.process(mViewInfoProcessCallback);
    }

    mLayout.removeAndRecycleScrapInt(mRecycler);
    mState.mPreviousLayoutItemCount = mState.mItemCount;
    mDataSetHasChangedAfterLayout = false;
    mDispatchItemsChangedEvent = false;
    mState.mRunSimpleAnimations = false;

    mState.mRunPredictiveAnimations = false;
    mLayout.mRequestedSimpleAnimations = false;
    if (mRecycler.mChangedScrap != null) {
        mRecycler.mChangedScrap.clear();
    }
    if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
        // Initial prefetch has expanded cache, so reset until next prefetch.
        // This prevents initial prefetches from expanding the cache permanently.
        mLayout.mPrefetchMaxCountObserved = 0;
        mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
        mRecycler.updateViewCacheSize();
    }
    //标记布局完成。
    mLayout.onLayoutCompleted(mState);
    onExitLayoutOrScroll();
    stopInterceptRequestLayout(false);
    //清除mViewInfoStore
    mViewInfoStore.clear();
    if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
        dispatchOnScrolled(0, 0);
    }
    recoverFocusFromState();
    resetFocusInfo();
}

动画相关信息我们先不看,到这里layout过程结束,下面继续看绘制过程。

RecyclerView的draw过程

RecyclerView重写了draw方法。

@Override
public void draw(Canvas c) {
    //注释1处
    super.draw(c);

    final int count = mItemDecorations.size();
    for (int i = 0; i < count; i++) {
        //注释2处,再次绘制分割线
        mItemDecorations.get(i).onDrawOver(c, this, mState);
    }
    //...  
}

@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);
    }
}

注释1处,调用了父类的draw方法。其中会先调用onDraw方法,RecyclerView重写了onDraw方法绘制了分割线。然后就是调用dispatchDraw方法在drawChild方法中绘制子View。然后在注释2处,再次绘制分割线。这也是为什么说我们自定义分割线的时候,只要重写ItemDecoration的onDraw或者onDrawOver一个方法就够了。两个都重写的话会导致绘制两次分割线。

接下来我们看一看在滑动和fling的时候,RecyclerView的一些逻辑。

RecyclerView的onTouchEvent方法

在滚动的时候涉及View的回收我们这里会注意一下。

@Override
public boolean onTouchEvent(MotionEvent e) {
    //精简大量代码...
    switch (action) {
       //...
        case MotionEvent.ACTION_MOVE: {
            final int index = e.findPointerIndex(mScrollPointerId);
            final int x = (int) (e.getX(index) + 0.5f);
            final int y = (int) (e.getY(index) + 0.5f);
            int dx = mLastTouchX - x;
            int dy = mLastTouchY - y;

            if (mScrollState != SCROLL_STATE_DRAGGING) {
                boolean startScroll = false;
                if (canScrollHorizontally) {
                   //横向滑动的忽略...
                }
                if (canScrollVertically) {
                    if (dy > 0) {
                        dy = Math.max(0, dy - mTouchSlop);
                    } else {
                        dy = Math.min(0, dy + mTouchSlop);
                    }
                    if (dy != 0) {
                        startScroll = true;
                    }
                }
                //到达了滑动的条件,将滑动状态置为SCROLL_STATE_DRAGGING
                if (startScroll) {
                    setScrollState(SCROLL_STATE_DRAGGING);
                }
            }

            if (mScrollState == SCROLL_STATE_DRAGGING) {
                //...
                //注释1处,
                if (scrollByInternal(
                        canScrollHorizontally ? dx : 0,
                        canScrollVertically ? dy : 0,
                        e)) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                //注释2处,这个先忽略,应该是处理多个RecyclerView的时候,预先获取ViewHolder的操作。
                if (mGapWorker != null && (dx != 0 || dy != 0)) {
                    mGapWorker.postFromTraversal(this, dx, dy);
                }
            }
        } break;

        case MotionEvent.ACTION_UP: {
            mVelocityTracker.addMovement(vtev);
            eventAddedToVelocityTracker = true;
            mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
            final float xvel = canScrollHorizontally
                    ? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
            final float yvel = canScrollVertically
                    ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
            //注释3处,如果速度够了,fling
            if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                    setScrollState(SCROLL_STATE_IDLE);
            }
            resetScroll();
        } break;
    }
    //...
    return true;
}

注释1处,调用RecyclerView的scrollByInternal方法。

 boolean scrollByInternal(int x, int y, MotionEvent ev) {
    //...
    if (mAdapter != null) {
        scrollStep(x, y, mReusableIntPair);
    }
    //...    
    return consumedNestedScroll || consumedX != 0 || consumedY != 0;
}

在scrollByInternal的方法内部,调用了调用RecyclerView的scrollStep方法。

void scrollStep(int dx, int dy, @Nullable int[] consumed) {
    //...
    //我们只看竖直方向上的滚动
    if (dy != 0) {
        consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
    }
}

调用LinearLayoutManager的scrollVerticallyBy方法。

@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
        RecyclerView.State state) {
    //...
    //注意这里会将layoutState的mScrollingOffset赋值为不是LayoutState.SCROLLING_OFFSET_NaN
    updateLayoutState(layoutDirection, absDelta, true, state);
    return scrollBy(dy, recycler, state);
}

LinearLayoutManager的scrollBy方法内部会调用fill方法填充子View,并将所有的子View偏移。

int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
    if (getChildCount() == 0 || delta == 0) {
        return 0;
    }
    ensureLayoutState();
    mLayoutState.mRecycle = true;
    //delta > 0,向下填充,layoutDirection为LayoutState.LAYOUT_END
    final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
    final int absDelta = Math.abs(delta);
    //注释0处,
    updateLayoutState(layoutDirection, absDelta, true, state);
    //注释1处,注意,这里继续调用fill方法填充子View。
    final int consumed = mLayoutState.mScrollingOffset
            + fill(recycler, mLayoutState, state, false);
    if (consumed < 0) {
        if (DEBUG) {
            Log.d(TAG, "Don't have any more elements to scroll");
        }
        return 0;
    }
    final int scrolled = absDelta > consumed ? layoutDirection * consumed : delta;
    //注释2处,偏移所有的子View
    mOrientationHelper.offsetChildren(-scrolled);
    if (DEBUG) {
        Log.d(TAG, "scroll req: " + delta + " scrolled: " + scrolled);
    }
    mLayoutState.mLastScrollDelta = scrolled;
    return scrolled;
}

注意:注释0处,调用了updateLayoutState方法,内部将mLayoutState.mScrollingOffset设置为一个不等于LayoutState.SCROLLING_OFFSET_NaN的值。在后面的fill方法里面,会判断mLayoutState.mScrollingOffset不等于LayoutState.SCROLLING_OFFSET_NaN的话,会进行View的回收操作。

/**
 * @param recycler        当前关联到RecyclerView的recycler。
 * @param layoutState     该如何填充可用空间的配置信息。
 * @param state           Context passed by the RecyclerView to control scroll steps.
 * @param stopOnFocusable 如果为true的话,遇到第一个可获取焦点的View则停止填充。
 * @return 返回添加的像素。
 */
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    // max offset we should set is mFastScroll + available
    final int start = layoutState.mAvailable;
    //...
    //注释0处,如果layoutState.mScrollingOffset不为SCROLLING_OFFSET_NaN的话,调用recycleByLayoutState方法,从这个方法名,我们可以看出来,这是一个回收View的方法。
    if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
        //...
        recycleByLayoutState(recycler, layoutState);
    }
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        layoutChunkResult.resetInternal();
        //注释1处,获取并添加子View,然后测量、布局子View并将分割线考虑在内。
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        //条件满足的话,跳出循环
        if (layoutChunkResult.mFinished) {
            break;
        }
        layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
        //注释2处,这里也会判断是否要调用recycleByLayoutState方法。
        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }
                recycleByLayoutState(recycler, layoutState);
            }
        if (stopOnFocusable && layoutChunkResult.mFocusable) {
            break;
        }
    }
    
    return start - layoutState.mAvailable;
}

注释0处和注释2处,如果layoutState.mScrollingOffset不为SCROLLING_OFFSET_NaN的话,调用recycleByLayoutState方法,从这个方法名,我们可以看出来,这是一个回收View的方法,具体细节先不管。

注释1处,获取并添加子View,然后测量、布局子View并将分割线考虑在内。其中涉及View的复用。

注释3处,看fling操作。

public boolean fling(int velocityX, int velocityY) {
     //精简大量代码
     mViewFlinger.fling(velocityX, velocityY);
     return true;
}

fling方法调用了mViewFlinger的fling方法来实现fling操作。

class ViewFlinger implements Runnable { 

    ViewFlinger() {
        mOverScroller = new OverScroller(getContext(), sQuinticInterpolator);
    }

    public void fling(int velocityX, int velocityY) {
        //设置滚动状态为SCROLL_STATE_SETTLING
        setScrollState(SCROLL_STATE_SETTLING);
        mLastFlingX = mLastFlingY = 0;
        //注释1处,计算坐标
        mOverScroller.fling(0, 0, velocityX, velocityY,
                Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
        //注释2处,
        postOnAnimation();
    }
    //...
}

ViewFlinger实现了Runnable接口。fling方法的注释1处,计算坐标,然后在注释2处调用postOnAnimation方法。postOnAnimation方法内部会将ViewFlinger作为一个Runnable对象post到一个消息队列中,然后在下一帧到来的时候,执行
ViewFlinger的run方法。

@Override
public void run() {
           
    mReSchedulePostAnimationCallback = false;
    mEatRunOnAnimationRequest = true;

    // Keep a local reference so that if it is changed during onAnimation method, it won't
    // cause unexpected behaviors
    final OverScroller scroller = mOverScroller;
    //注释1处,如果滚动没有结束
    if (scroller.computeScrollOffset()) {
        //精简大量代码...
        if (mAdapter != null) {
            mReusableIntPair[0] = 0;
            mReusableIntPair[1] = 0;
            //注释2处,重点
            scrollStep(unconsumedX, unconsumedY, mReusableIntPair);
            //...
        }
                
        if (!mItemDecorations.isEmpty()) {
            invalidate();
        }

        boolean scrollerFinishedX = scroller.getCurrX() == scroller.getFinalX();
        boolean scrollerFinishedY = scroller.getCurrY() == scroller.getFinalY();
        //注释3处,是否已经结束了
        final boolean doneScrolling = scroller.isFinished()
                || ((scrollerFinishedX || unconsumedX != 0)
                && (scrollerFinishedY || unconsumedY != 0));

        SmoothScroller smoothScroller = mLayout.mSmoothScroller;
        boolean smoothScrollerPending =
                 smoothScroller != null && smoothScroller.isPendingInitialRun();

        if (!smoothScrollerPending && doneScrolling) {
           //注释4处,滚动结束了...
        } else {
            //注释5处,滚动没有结束,继续post。
            postOnAnimation();
            //...
        }
    }

    mEatRunOnAnimationRequest = false;
    //注释6处,mReSchedulePostAnimationCallback为true,
    if (mReSchedulePostAnimationCallback) {
        internalPostOnAnimation();
    } else {
        //注释7处,滚动结束,不需要post了。
        setScrollState(SCROLL_STATE_IDLE);
        stopNestedScroll(TYPE_NON_TOUCH);
    }
}

注释1处,如果滚动没有结束。

注释2处,重点,调用scrollStep方法,内部会最终调用fill方法填充子View,并将所有的子View偏移。

注释3处,判断是否已经结束了(这里有点不明白,不是滚动没结束才进入这里吗,为什么还要重复判断,这里猜测是因为嵌套滑动的问题,我们忽略)。

注释5处,滚动没有结束,继续post,在下一帧到来的时候继续执行ViewFlinger的run方法。

void postOnAnimation() {
    if (mEatRunOnAnimationRequest) {
        mReSchedulePostAnimationCallback = true;
    } else {
        internalPostOnAnimation();
    }
}

注意,因为这时候mEatRunOnAnimationRequest为true,所以postOnAnimation只是将mReSchedulePostAnimationCallback置为了true,并没有调用internalPostOnAnimation方法真正post。

注释6处,mReSchedulePostAnimationCallback为true,调用internalPostOnAnimation方法真正post。

总结:

  • RecyclerView的布局操作分为三步。dispatchLayoutStep1dispatchLayoutStep2dispatchLayoutStep3dispatchLayoutStep1dispatchLayoutStep3是用来处理动画相关的逻辑。dispatchLayoutStep2中会真正调用LayoutManager的onLayoutChildren方法来布局子View。

  • LayoutManager的onLayoutChildren方法算法

  1. 通过检查children和其他变量,找到一个锚点坐标和锚点的位置(锚点View对应在adapter中数据对应的位置)。 默认情况下锚点就是代表适配器中第一个数据对应的ViewHolder。
  2. 从锚点向上填充RecyclerView。
  3. 从锚点向下填充RecyclerView。
  4. 滚动RecyclerView,做一些显示上的调整。
  • RecyclerView通过偏移所有的子View来实现滚动效果。在滚动操作过程中,会发生View的回收和复用。这个过程发生在LayoutManager的fill方法中。

参考链接:

  • RecyclerView 源码分析(一) - RecyclerView的三大流程
  • 【进阶】RecyclerView源码解析(一)——绘制流程

你可能感兴趣的:(RecyclerView源码分析之一)