从源码解析RecyclerView绘制流程

来源:互联网 发布:淘宝商家货到付款 编辑:程序博客网 时间:2024/05/16 10:59

  Recycler View的出现,无疑是令android 开发工程师兴奋了许久。不久前看ListView 源码的时候,看得晕乎乎的,滑动功能以及复用机制堆积在了同一个类里面,RecyclerView 很好的解决这种尴尬的情况。

  下面先从功能上看一下各个类的功能: 

      RecyclerView 这个类相当于中心的管理者

      LayoutManager  主要负责item 添加 ,删除,以及循环利用机制等功能

      Recycler.SmoothScroller 主要是负责跟踪目标view的index 和 平滑滑动

     Recycler.UpdateOp    主要负责标志view的行为,增加 删除   更新

     Recycler.ViewHolder 这个类大家都很熟悉,没错他就是相当于ListView 的adapter 中的ViewHolder 

     Recycler.State  主要是保留RecyclerView的一些信息。

     Recycler.Adapter  主要使将dataset数据与itemView 绑定在一起

    Recycler.Recycler  主要是保留正在显示的View和已经废弃或者是可回收利用的view

上面是主要的类,这篇文章中,我们不涉及RecyclerView 相关的动画

    入口函数是setAdapter 或者是setLayoutManager ,我们 用其中一个即可,下面是

   

 public void setAdapter(Adapter adapter) {        if (mAdapter != null) {            mAdapter.unregisterAdapterDataObserver(mObserver);        }        // end all running animations        if (mItemAnimator != null) {            mItemAnimator.endAnimations();        }        // Since animations are ended, mLayout.children should be equal to recyclerView.children.        // This may not be true if item animator's end does not work as expected. (e.g. not release        // children instantly). It is safer to use mLayout's child count.        if (mLayout != null) {            mLayout.removeAndRecycleAllViews(mRecycler);            mLayout.removeAndRecycleScrapInt(mRecycler, true);        }        final Adapter oldAdapter = mAdapter;        mAdapter = adapter;        if (adapter != null) {            adapter.registerAdapterDataObserver(mObserver);        }        if (mLayout != null) {            mLayout.onAdapterChanged(oldAdapter, mAdapter);        }        mRecycler.onAdapterChanged(oldAdapter, mAdapter);        mState.mStructureChanged = true;        markKnownViewsInvalid();        requestLayout();    }
      最后requestLayout 会调用RecyclerView的onMeasure 和 onLayout() 方法 

     

 @Override    protected void onMeasure(int widthSpec , int heightSpec) {        if (mAdapterUpdateDuringMeasure) {            eatRequestLayout();            updateChildViews();            mAdapterUpdateDuringMeasure = false;            resumeRequestLayout(false);        }        if (mAdapter != null) {            mState.mItemCount = mAdapter.getItemCount();        }        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);        final int widthSize = getMeasuredWidth();        final int heightSize = getMeasuredHeight();        if (mLeftGlow != null) {            mLeftGlow.setSize(heightSize, widthSize);        }        if (mTopGlow != null) {            mTopGlow.setSize(widthSize, heightSize);        }        if (mRightGlow != null) {            mRightGlow.setSize(heightSize, widthSize);        }        if (mBottomGlow != null) {            mBottomGlow.setSize(widthSize, heightSize);        }    }

   mAdapterUpdateDuringMeasure  这个变量在下一篇文章中会追踪一下,我们这里就看主要的函数,下面我们来看一下,LinearLayoutManager 的 onMeasure 方法 

 

        public void onMeasure(Recycler recycler , State state , int widthSpec , int heightSpec) {            final int widthMode = MeasureSpec.getMode(widthSpec);            final int heightMode = MeasureSpec.getMode(heightSpec);            final int widthSize = MeasureSpec.getSize(widthSpec);            final int heightSize = MeasureSpec.getSize(heightSpec);            int width = 0;            int height = 0;            switch (widthMode) {                case MeasureSpec.EXACTLY:                case MeasureSpec.AT_MOST:                    width = widthSize;                    break;                case MeasureSpec.UNSPECIFIED:                default:                    width = getMinimumWidth();                    break;            }            switch (heightMode) {                case MeasureSpec.EXACTLY:                case MeasureSpec.AT_MOST:                    height = heightSize;                    break;                case MeasureSpec.UNSPECIFIED:                default:                    height = getMinimumHeight();                    break;            }            setMeasuredDimension(width, height);        }

   可以看出,主要就是来设置RecyclerView的Size,那么下面来看一下RecyclerView的 Onlayout方法

   

   

@Override    protected void onLayout(boolean changed , int l , int t , int r , int b) {        eatRequestLayout();        dispatchLayout();        resumeRequestLayout(false);        mFirstLayoutComplete = true;    }
   看一下dispatchLayout()方法

 

void dispatchLayout() {        if (mAdapter == null) {            Log.e(TAG, "No adapter attached; skipping layout");            return;        }        eatRequestLayout();        // simple animations are a subset of advanced animations (which will cause a        // prelayout step)        boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved && !mItemsChanged;        final boolean animateChangesAdvanced = ENABLE_PREDICTIVE_ANIMATIONS && animateChangesSimple && predictiveItemAnimationsEnabled();        mItemsAddedOrRemoved = mItemsChanged = false;        ArrayMap<View, Rect> appearingViewInitialBounds = null;        mState.mInPreLayout = animateChangesAdvanced;        mState.mItemCount = mAdapter.getItemCount();        if (animateChangesSimple) {            // Step 0: Find out where all non-removed items are, pre-layout            mState.mPreLayoutHolderMap.clear();            mState.mPostLayoutHolderMap.clear();            final int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), holder.mPosition));            }        }        if (animateChangesAdvanced) {            // Step 1: run prelayout: This will use the old positions of items. The layout manager            // is expected to layout everything, even removed items (though not to add removed            // items back to the container). This gives the pre-layout position of APPEARING views            // which come into existence as part of the real layout.            mInPreLayout = true;            final boolean didStructureChange = mState.mStructureChanged;            mState.mStructureChanged = false;            // temporarily disable flag because we are asking for previous layout            mLayout.onLayoutChildren(mRecycler, mState);            mState.mStructureChanged = didStructureChange;            mInPreLayout = false;            appearingViewInitialBounds = new ArrayMap<View, Rect>();            for (int i = 0; i < getChildCount(); ++i) {                boolean found = false;                final View child = getChildAt(i);                for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {                    final ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);                    if (holder.itemView == child) {                        found = true;                        continue;                    }                }                if (!found) {                    appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()));                }            }        }        clearOldPositions();        dispatchLayoutUpdates();        mState.mItemCount = mAdapter.getItemCount();        // Step 2: Run layout        mState.mInPreLayout = false;        mLayout.onLayoutChildren(mRecycler, mState);        mState.mStructureChanged = false;        mPendingSavedState = null;        // onLayoutChildren may have caused client code to disable item animations; re-check        animateChangesSimple = animateChangesSimple && mItemAnimator != null;        if (animateChangesSimple) {            // Step 3: Find out where things are now, post-layout            int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), holder.mPosition));            }            // Step 4: Animate DISAPPEARING and REMOVED items            final int preLayoutCount = mState.mPreLayoutHolderMap.size();            for (int i = preLayoutCount - 1; i >= 0; i--) {                final ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);                if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {                    final ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);                    mState.mPreLayoutHolderMap.removeAt(i);                    final View disappearingItemView = disappearingItem.holder.itemView;                    removeDetachedView(disappearingItemView, false);                    mRecycler.unscrapView(disappearingItem.holder);                    animateDisappearance(disappearingItem);                }            }            // Step 5: Animate APPEARING and ADDED items            final int postLayoutCount = mState.mPostLayoutHolderMap.size();            if (postLayoutCount > 0) {                for (int i = postLayoutCount - 1; i >= 0; i--) {                    final ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);                    final ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);                    if ((mState.mPreLayoutHolderMap.isEmpty() || !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {                        mState.mPostLayoutHolderMap.removeAt(i);                        final Rect initialBounds = (appearingViewInitialBounds != null) ? appearingViewInitialBounds.get(itemHolder.itemView) : null;                        animateAppearance(itemHolder, initialBounds, info.left, info.top);                    }                }            }            // Step 6: Animate PERSISTENT items            count = mState.mPostLayoutHolderMap.size();            for (int i = 0; i < count; ++i) {                final ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);                final ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);                final ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);                if (preInfo != null && postInfo != null) {                    if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {                        postHolder.setIsRecyclable(false);                        if (DEBUG) {                            Log.d(TAG, "PERSISTENT: " + postHolder + " with view " + postHolder.itemView);                        }                        if (mItemAnimator.animateMove(postHolder, preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {                            postAnimationRunner();                        }                    }                }            }        }        resumeRequestLayout(false);        mLayout.removeAndRecycleScrapInt(mRecycler, !animateChangesAdvanced);        mState.mPreviousLayoutItemCount = mState.mItemCount;        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;    }
 这段代码看起来很多,很多都是有关动画,今天我们的重点不是动画,所以找出其中的关键代码,

       // Step 2: Run layout
        mState.mInPreLayout = false;
        mLayout.onLayoutChildren(mRecycler, mState);

主要的layout 部分,我们来看一下LayoutManager的onLayoutChildren 方法,

    

 public void onLayoutChildren(RecyclerView.Recycler recycler , RecyclerView.State state) {        // layout algorithm:        // 1) by checking children and other variables, find an anchor coordinate and an anchor        // item position.        // 2) fill towards start, stacking from bottom        // 3) fill towards end, stacking from top        // 4) scroll to fulfill requirements like stack from bottom.        // create render state        if (DEBUG) {            Log.d(TAG, "is pre layout:" + state.isPreLayout());        }        if (mPendingSavedState != null) {            setOrientation(mPendingSavedState.mOrientation);            setReverseLayout(mPendingSavedState.mReverseLayout);            setStackFromEnd(mPendingSavedState.mStackFromEnd);            mPendingScrollPosition = mPendingSavedState.mAnchorPosition;        }        ensureRenderState();        // resolve layout direction        resolveShouldLayoutReverse();        // validate scroll position if exists        if (mPendingScrollPosition != RecyclerView.NO_POSITION) {            // validate it            if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {                mPendingScrollPosition = RecyclerView.NO_POSITION;                mPendingScrollPositionOffset = INVALID_OFFSET;                if (DEBUG) {                    Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition);                }            }        }        // this value might be updated if there is a target scroll position without an offset        boolean layoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;        final boolean stackFromEndChanged = mLastStackFromEnd != mStackFromEnd;        int anchorCoordinate, anchorItemPosition;        if (mPendingScrollPosition != RecyclerView.NO_POSITION) {            // if child is visible, try to make it a reference child and ensure it is fully visible.            // if child is not visible, align it depending on its virtual position.            anchorItemPosition = mPendingScrollPosition;            if (mPendingSavedState != null) {                // Anchor offset depends on how that child was laid out. Here, we update it                // according to our current view bounds                layoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;                if (layoutFromEnd) {                    anchorCoordinate = mOrientationHelper.getEndAfterPadding() - mPendingSavedState.mAnchorOffset;                }                else {                    anchorCoordinate = mOrientationHelper.getStartAfterPadding() + mPendingSavedState.mAnchorOffset;                }            }            else if (mPendingScrollPositionOffset == INVALID_OFFSET) {                final View child = findViewByPosition(mPendingScrollPosition);                if (child != null) {                    final int startGap = mOrientationHelper.getDecoratedStart(child) - mOrientationHelper.getStartAfterPadding();                    final int endGap = mOrientationHelper.getEndAfterPadding() - mOrientationHelper.getDecoratedEnd(child);                    final int childSize = mOrientationHelper.getDecoratedMeasurement(child);                    if (childSize > mOrientationHelper.getTotalSpace()) {                        // item does not fit. fix depending on layout direction                        anchorCoordinate = layoutFromEnd ? mOrientationHelper.getEndAfterPadding() : mOrientationHelper.getStartAfterPadding();                    }                    else if (startGap < 0) {                        anchorCoordinate = mOrientationHelper.getStartAfterPadding();                        layoutFromEnd = false;                    }                    else if (endGap < 0) {                        anchorCoordinate = mOrientationHelper.getEndAfterPadding();                        layoutFromEnd = true;                    }                    else {                        anchorCoordinate = layoutFromEnd ? mOrientationHelper.getDecoratedEnd(child) : mOrientationHelper.getDecoratedStart(child);                    }                }                else { // item is not visible.                    if (getChildCount() > 0) {                        // get position of any child, does not matter                        final int pos = getPosition(getChildAt(0));                        if (mPendingScrollPosition < pos == mShouldReverseLayout) {                            anchorCoordinate = mOrientationHelper.getEndAfterPadding();                            layoutFromEnd = true;                        }                        else {                            anchorCoordinate = mOrientationHelper.getStartAfterPadding();                            layoutFromEnd = false;                        }                    }                    else {                        anchorCoordinate = layoutFromEnd ? mOrientationHelper.getEndAfterPadding() : mOrientationHelper.getStartAfterPadding();                    }                }            }            else {                // override layout from end values for consistency                if (mShouldReverseLayout) {                    anchorCoordinate = mOrientationHelper.getEndAfterPadding() - mPendingScrollPositionOffset;                    layoutFromEnd = true;                }                else {                    anchorCoordinate = mOrientationHelper.getStartAfterPadding() + mPendingScrollPositionOffset;                    layoutFromEnd = false;                }            }        }        else if (getChildCount() > 0 && !stackFromEndChanged) {            if (layoutFromEnd) {                final View referenceChild = getChildClosestToEnd();                anchorCoordinate = mOrientationHelper.getDecoratedEnd(referenceChild);                anchorItemPosition = getPosition(referenceChild);            }            else {                final View referenceChild = getChildClosestToStart();                anchorCoordinate = mOrientationHelper.getDecoratedStart(referenceChild);                anchorItemPosition = getPosition(referenceChild);            }        }        else {            anchorCoordinate = layoutFromEnd ? mOrientationHelper.getEndAfterPadding() : mOrientationHelper.getStartAfterPadding();            anchorItemPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;        }        detachAndScrapAttachedViews(recycler);        final int extraForStart;        final int extraForEnd;        final int extra = getExtraLayoutSpace(state);        final boolean before = state.getTargetScrollPosition() < anchorItemPosition;        if (before == mShouldReverseLayout) {            extraForEnd = extra;            extraForStart = 0;        }        else {            extraForStart = extra;            extraForEnd = 0;        }        // first fill towards start        updateRenderStateToFillStart(anchorItemPosition, anchorCoordinate);        mRenderState.mExtra = extraForStart;        if (!layoutFromEnd) {            mRenderState.mCurrentPosition += mRenderState.mItemDirection;        }        fill(recycler, mRenderState, state, false);        int startOffset = mRenderState.mOffset;        // fill towards end        updateRenderStateToFillEnd(anchorItemPosition, anchorCoordinate);        mRenderState.mExtra = extraForEnd;        if (layoutFromEnd) {            mRenderState.mCurrentPosition += mRenderState.mItemDirection;        }        fill(recycler, mRenderState, state, false);        int endOffset = mRenderState.mOffset;        // changes may cause gaps on the UI, try to fix them.        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) {                int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);                startOffset += fixOffset;                endOffset += fixOffset;                fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);                startOffset += fixOffset;                endOffset += fixOffset;            }            else {                int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);                startOffset += fixOffset;                endOffset += fixOffset;                fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);                startOffset += fixOffset;                endOffset += fixOffset;            }        }        // If there are scrap children that we did not layout, we need to find where they did go        // and layout them accordingly so that animations can work as expected.        // This case may happen if new views are added or an existing view expands and pushes        // another view out of bounds.        if (getChildCount() > 0 && !state.isPreLayout() && supportsPredictiveItemAnimations()) {            // to make the logic simpler, we calculate the size of children and call fill.            int scrapExtraStart = 0, scrapExtraEnd = 0;            final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();            final int scrapSize = scrapList.size();            final int firstChildPos = getPosition(getChildAt(0));            for (int i = 0; i < scrapSize; i++) {                final RecyclerView.ViewHolder scrap = scrapList.get(i);                final int position = scrap.getPosition();                final int direction = position < firstChildPos != mShouldReverseLayout ? RenderState.LAYOUT_START : RenderState.LAYOUT_END;                if (direction == RenderState.LAYOUT_START) {                    scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);                }                else {                    scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);                }            }            if (DEBUG) {                Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart + " towards start and " + scrapExtraEnd + " towards end");            }            mRenderState.mScrapList = scrapList;            if (scrapExtraStart > 0) {                final View anchor = getChildClosestToStart();                updateRenderStateToFillStart(getPosition(anchor), startOffset);                mRenderState.mExtra = scrapExtraStart;                mRenderState.mAvailable = 0;                mRenderState.mCurrentPosition += mShouldReverseLayout ? 1 : -1;                fill(recycler, mRenderState, state, false);            }            if (scrapExtraEnd > 0) {                final View anchor = getChildClosestToEnd();                updateRenderStateToFillEnd(getPosition(anchor), endOffset);                mRenderState.mExtra = scrapExtraEnd;                mRenderState.mAvailable = 0;                mRenderState.mCurrentPosition += mShouldReverseLayout ? -1 : 1;                fill(recycler, mRenderState, state, false);            }            mRenderState.mScrapList = null;        }        mPendingScrollPosition = RecyclerView.NO_POSITION;        mPendingScrollPositionOffset = INVALID_OFFSET;        mLastStackFromEnd = mStackFromEnd;        mPendingSavedState = null; // we don't need this anymore        if (DEBUG) {            validateChildOrder();        }    }
 这段代码已经在开始的时候标注了逻辑

  

  // layout algorithm:        // 1) by checking children and other variables, find an anchor coordinate and an anchor        // item position.        // 2) fill towards start, stacking from bottom        // 3) fill towards end, stacking from top        // 4) scroll to fulfill requirements like stack from bottom.

     而2 ,3 就是关键的地方 ,这里包含循环利用的逻辑,那我们接下来就看这段代码。

    /**     * The magic functions :). Fills the given layout, defined by the renderState. This is fairly independent from the     * rest of the {@link android.support.v7.widget.LinearLayoutManager} and with little change, can be made publicly     * available as a helper class.     *      * @param recycler Current recycler that is attached to RecyclerView     * @param renderState Configuration on how we should fill out the available space.     * @param state Context passed by the RecyclerView to control scroll steps.     * @param stopOnFocusable If true, filling stops in the first focusable new child     * @return Number of pixels that it added. Useful for scoll functions.     */    private int fill(RecyclerView.Recycler recycler , RenderState renderState , RecyclerView.State state , boolean stopOnFocusable) {        // max offset we should set is mFastScroll + available        final int start = renderState.mAvailable;        if (renderState.mScrollingOffset != RenderState.SCOLLING_OFFSET_NaN) {            // TODO ugly bug fix. should not happen            if (renderState.mAvailable < 0) {                renderState.mScrollingOffset += renderState.mAvailable;            }            recycleByRenderState(recycler, renderState);        }        int remainingSpace = renderState.mAvailable + renderState.mExtra;        while (remainingSpace > 0 && renderState.hasMore(state)) {            final View view = renderState.next(recycler);            if (view == null) {                if (DEBUG) {                    if (renderState.mScrapList == null) {                        throw new RuntimeException("received null view when unexpected");                    }                }                // if we are laying out views in scrap, this may return null which means there is                // no more items to layout.                break;            }            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();            if (!params.isItemRemoved() && mRenderState.mScrapList == null) {                if (mShouldReverseLayout == (renderState.mLayoutDirection == RenderState.LAYOUT_START)) {                    addView(view);                }                else {                    addView(view, 0);                }            }            measureChildWithMargins(view, 0, 0);            final int consumed = 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 (renderState.mLayoutDirection == RenderState.LAYOUT_START) {                    bottom = renderState.mOffset;                    top = renderState.mOffset - consumed;                }                else {                    top = renderState.mOffset;                    bottom = renderState.mOffset + consumed;                }            }            else {                top = getPaddingTop();                bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);                if (renderState.mLayoutDirection == RenderState.LAYOUT_START) {                    right = renderState.mOffset;                    left = renderState.mOffset - consumed;                }                else {                    left = renderState.mOffset;                    right = renderState.mOffset + consumed;                }            }            // We calculate everything with View's bounding box (which includes decor and margins)            // To calculate correct layout position, we subtract margins.            layoutDecorated(view, left + params.leftMargin, top + params.topMargin, right - params.rightMargin, bottom - params.bottomMargin);            if (DEBUG) {                Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:" + (left + params.leftMargin) + ", t:"                        + (top + params.topMargin) + ", r:" + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));            }            renderState.mOffset += consumed * renderState.mLayoutDirection;            if (!params.isItemRemoved()) {                renderState.mAvailable -= consumed;                // we keep a separate remaining space because mAvailable is important for recycling                remainingSpace -= consumed;            }            if (renderState.mScrollingOffset != RenderState.SCOLLING_OFFSET_NaN) {                renderState.mScrollingOffset += consumed;                if (renderState.mAvailable < 0) {                    renderState.mScrollingOffset += renderState.mAvailable;                }                recycleByRenderState(recycler, renderState);            }            if (stopOnFocusable && view.isFocusable()) {                break;            }            if (state != null && state.getTargetScrollPosition() == getPosition(view)) {                break;            }        }        if (DEBUG) {            validateChildOrder();        }        return start - renderState.mAvailable;    }
    

     在fill 之前,先回收一下无用的数据(超出屏幕的itemView),然后使用 while (remainingSpace > 0 && renderState.hasMore(state))  循环,添加数据直至超出屏幕。

 这里主要就是循环复用机制 ,假如之前阅读过listview 循环复用机制,这里和listview是类似的。

     这就是RecyclerView 的基础绘画流程


1 0
原创粉丝点击