您的位置:首页 > 其它

从源码解析RecyclerView绘制流程

2015-05-27 11:35 531 查看
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 的基础绘画流程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐