Android高级-阿里VLayout使用和原理分析

VLayout 概念

定义:

VLayout全程VirtuaLayout,它是一个针对RecyclerView的LayoutManager扩展,主要提供一整套布局方案

和布局间的组件复用的问题。由阿里团队开发,已经运用在手机淘宝项目上。2017年3月份开源。它通过定制化的LayoutManager,

接管整个RecyclerView布局逻辑。

使用:

  implementation 'com.alibaba.android:vlayout:1.0.3'

自己创建一个Adapter


public class BaseDelegeteAdapter extends DelegateAdapter.Adapter<BaseViewHolder>{

    private LayoutHelper mLayoutHelper;
    private int mCount = -1;
    private int mLayoutId = -1;
    private Context mContext;
    private int mViewTypeItem = -1;
    public BaseDelegeteAdapter(Context context,
                               LayoutHelper layoutHelper,
                               int layoutId, int count ) {
        this.mLayoutHelper = layoutHelper;
        this.mCount = count;
        this.mLayoutId = layoutId;
        this.mContext = context;
    }
    @Override
    public LayoutHelper onCreateLayoutHelper() {
        return mLayoutHelper;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
            return  new BaseViewHolder(LayoutInflater.from(mContext).inflate(mLayoutId, viewGroup, false));
    }

    @Override
    public void onBindViewHolder(@NonNull BaseViewHolder baseViewHolder, int i) {

    }

    @Override
    public int getItemCount() {
        return mCount;
    }
}

继承自DelegateAdapter.Adapter

这个代码中有下面的抽象类,这个类主要是去创建LayoutHelper 

  public static abstract class Adapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
        public abstract LayoutHelper onCreateLayoutHelper();

        protected void onBindViewHolderWithOffset(VH holder, int position, int offsetTotal) {

        }
    }

而LayoutHelper 有很多子类

/**
 * Helper class to handle different layouts in {@link VirtualLayoutManager}
 *
 * @author villadora
 * @date 2015-8-14
 * @since 1.0.0
 */

public abstract class LayoutHelper {

 

很多种不同的布局样式可以供开发去选择。--这也是Vlayout的机密所在。

平常的RecyclView只支持一个适配器,但是Vlaytout可以支持很多个Adapter

而通过DelegateAdapter相当于一个总管,把这些adapter都添加到自己里,

 DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager, true);
        delegateAdapter.addAdapter(bannerAdapter);
        delegateAdapter.addAdapter(menuAdapter);

 然后再把adaptr设置进RecyclerView

     mRecyclerView.setAdapter(delegateAdapter);

什么是排版原理:

VLayout的例子:

Vlayout的意义:

 

布局排版方式种类:

而VLayout支持这些布局(你可以尽情的任意实现界面需求)

二 :VLayout源码解析:

适配器分发精髓详解

首先我们先去看RecyclerView

RecyclerView的OnLayout方法;

    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        TraceCompat.beginSection("RV OnLayout");
        this.dispatchLayout();
        TraceCompat.endSection();
        this.mFirstLayoutComplete = true;
    }
  void dispatchLayout() {
        if(this.mAdapter == null) {
            Log.e("RecyclerView", "No adapter attached; skipping layout");
        } else if(this.mLayout == null) {
            Log.e("RecyclerView", "No layout manager attached; skipping layout");
        } else {
            this.mState.mIsMeasuring = false;
            if(this.mState.mLayoutStep == 1) {
                this.dispatchLayoutStep1();
                this.mLayout.setExactMeasureSpecsFrom(this);
                //分发调用的第二步
                this.dispatchLayoutStep2();
            } else if(!this.mAdapterHelper.hasUpdates() && this.mLayout.getWidth() == this.getWidth() && this.mLayout.getHeight() == this.getHeight()) {
                this.mLayout.setExactMeasureSpecsFrom(this);
            } else {
                this.mLayout.setExactMeasureSpecsFrom(this);
                this.dispatchLayoutStep2();
            }

            this.dispatchLayoutStep3();
        }
    }
    private void dispatchLayoutStep2() {
        this.startInterceptRequestLayout();
        this.onEnterLayoutOrScroll();
        this.mState.assertLayoutStep(6);
        this.mAdapterHelper.consumeUpdatesInOnePass();
        this.mState.mItemCount = this.mAdapter.getItemCount();
        this.mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
        this.mState.mInPreLayout = false;
       //注意这个方法,这个mLayout是哪谁呢
        this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
        this.mState.mStructureChanged = false;
        this.mPendingSavedState = null;
        this.mState.mRunSimpleAnimations = this.mState.mRunSimpleAnimations && this.mItemAnimator != null;
        this.mState.mLayoutStep = 4;
        this.onExitLayoutOrScroll();
        this.stopInterceptRequestLayout(false);
    }
RecyclerView.LayoutManager mLayout; 这个是一个LayoutManager,是外部传进来的,
 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            Log.e("RecyclerView", "You must override onLayoutChildren(Recycler recycler, State state) ");
        }

这个是由传进来的LayoutManager去实现具体的方法,例如LinearLayoutManager

思路就是让你自己去填充,我们之前手写RecyclerView的时候,是循环将布局item添加到adapter上,是在RecyclerView内部实现的,但是在RecyclerView内部机制是交给了其他角色去实现


    public void onLayoutChildren(Recycler recycler, State state) {
        if((this.mPendingSavedState != null || this.mPendingScrollPosition != -1) && state.getItemCount() == 0) {
            this.removeAndRecycleAllViews(recycler);
        } else {
            if(this.mPendingSavedState != null && this.mPendingSavedState.hasValidAnchor()) {
                this.mPendingScrollPosition = this.mPendingSavedState.mAnchorPosition;
            }

            this.ensureLayoutState();
            this.mLayoutState.mRecycle = false;
            this.resolveShouldLayoutReverse();
            View focused = this.getFocusedChild();
            if(this.mAnchorInfo.mValid && this.mPendingScrollPosition == -1 && this.mPendingSavedState == null) {
                if(focused != null && (this.mOrientationHelper.getDecoratedStart(focused) >= this.mOrientationHelper.getEndAfterPadding() || this.mOrientationHelper.getDecoratedEnd(focused) <= this.mOrientationHelper.getStartAfterPadding())) {
                    this.mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, this.getPosition(focused));
                }
            } else {
                this.mAnchorInfo.reset();
                this.mAnchorInfo.mLayoutFromEnd = this.mShouldReverseLayout ^ this.mStackFromEnd;
                this.updateAnchorInfoForLayout(recycler, state, this.mAnchorInfo);
                this.mAnchorInfo.mValid = true;
            }

            int extra = this.getExtraLayoutSpace(state);
            int extraForStart;
            int extraForEnd;
            if(this.mLayoutState.mLastScrollDelta >= 0) {
                extraForEnd = extra;
                extraForStart = 0;
            } else {
                extraForStart = extra;
                extraForEnd = 0;
            }

            extraForStart += this.mOrientationHelper.getStartAfterPadding();
            extraForEnd += this.mOrientationHelper.getEndPadding();
            int endOffset;
            int upcomingOffset;
            if(state.isPreLayout() && this.mPendingScrollPosition != -1 && this.mPendingScrollPositionOffset != -2147483648) {
                View existing = this.findViewByPosition(this.mPendingScrollPosition);
                if(existing != null) {
                    if(this.mShouldReverseLayout) {
                        endOffset = this.mOrientationHelper.getEndAfterPadding() - this.mOrientationHelper.getDecoratedEnd(existing);
                        upcomingOffset = endOffset - this.mPendingScrollPositionOffset;
                    } else {
                        endOffset = this.mOrientationHelper.getDecoratedStart(existing) - this.mOrientationHelper.getStartAfterPadding();
                        upcomingOffset = this.mPendingScrollPositionOffset - endOffset;
                    }

                    if(upcomingOffset > 0) {
                        extraForStart += upcomingOffset;
                    } else {
                        extraForEnd -= upcomingOffset;
                    }
                }
            }

            if(this.mAnchorInfo.mLayoutFromEnd) {
                upcomingOffset = this.mShouldReverseLayout?1:-1;
            } else {
                upcomingOffset = this.mShouldReverseLayout?-1:1;
            }

            this.onAnchorReady(recycler, state, this.mAnchorInfo, upcomingOffset);
            this.detachAndScrapAttachedViews(recycler);
            this.mLayoutState.mInfinite = this.resolveIsInfinite();
            this.mLayoutState.mIsPreLayout = state.isPreLayout();
            int fixOffset;
            int startOffset;
            if(this.mAnchorInfo.mLayoutFromEnd) {
                this.updateLayoutStateToFillStart(this.mAnchorInfo);
                this.mLayoutState.mExtra = extraForStart;
                this.fill(recycler, this.mLayoutState, state, false);
                startOffset = this.mLayoutState.mOffset;
                fixOffset = this.mLayoutState.mCurrentPosition;
                if(this.mLayoutState.mAvailable > 0) {
                    extraForEnd += this.mLayoutState.mAvailable;
                }

                this.updateLayoutStateToFillEnd(this.mAnchorInfo);
                this.mLayoutState.mExtra = extraForEnd;
                this.mLayoutState.mCurrentPosition += this.mLayoutState.mItemDirection;
                this.fill(recycler, this.mLayoutState, state, false);
                endOffset = this.mLayoutState.mOffset;
                if(this.mLayoutState.mAvailable > 0) {
                    extraForStart = this.mLayoutState.mAvailable;
                    this.updateLayoutStateToFillStart(fixOffset, startOffset);
                    this.mLayoutState.mExtra = extraForStart;
                    this.fill(recycler, this.mLayoutState, state, false);
                    startOffset = this.mLayoutState.mOffset;
                }
            } else {
                this.updateLayoutStateToFillEnd(this.mAnchorInfo);
                this.mLayoutState.mExtra = extraForEnd;
                this.fill(recycler, this.mLayoutState, state, false);
                endOffset = this.mLayoutState.mOffset;
                fixOffset = this.mLayoutState.mCurrentPosition;
                if(this.mLayoutState.mAvailable > 0) {
                    extraForStart += this.mLayoutState.mAvailable;
                }

                this.updateLayoutStateToFillStart(this.mAnchorInfo);
                this.mLayoutState.mExtra = extraForStart;
                this.mLayoutState.mCurrentPosition += this.mLayoutState.mItemDirection;
                this.fill(recycler, this.mLayoutState, state, false);
                startOffset = this.mLayoutState.mOffset;
                if(this.mLayoutState.mAvailable > 0) {
                    extraForEnd = this.mLayoutState.mAvailable;
                    this.updateLayoutStateToFillEnd(fixOffset, endOffset);
                    this.mLayoutState.mExtra = extraForEnd;
                    this.fill(recycler, this.mLayoutState, state, false);
                    endOffset = this.mLayoutState.mOffset;
                }
            }

            if(this.getChildCount() > 0) {
                if(this.mShouldReverseLayout ^ this.mStackFromEnd) {
                    fixOffset = this.fixLayoutEndGap(endOffset, recycler, state, true);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                    fixOffset = this.fixLayoutStartGap(startOffset, recycler, state, false);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                } else {
                    fixOffset = this.fixLayoutStartGap(startOffset, recycler, state, true);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                    fixOffset = this.fixLayoutEndGap(endOffset, recycler, state, false);
                    startOffset += fixOffset;
                    endOffset += fixOffset;
                }
            }

            this.layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
            if(!state.isPreLayout()) {
                this.mOrientationHelper.onLayoutComplete();
            } else {
                this.mAnchorInfo.reset();
            }

            this.mLastStackFromEnd = this.mStackFromEnd;
        }
    }

而VirtualLayoutManager他的继承结果  我们看看

public class VirtualLayoutManager extends ExposeLinearLayoutManagerEx implements LayoutManagerHelper {
class ExposeLinearLayoutManagerEx extends LinearLayoutManager {

VirtualLayoutManager实现了LinearLayoutManager 这是为什么,我们知道LinearLayoutManager中的一拖三布局和其他布局都是自上而下的布局,只不过,item可以实现不同的效果而已

我们看一下ExposeLinearLayoutManagerEx 里面实现了onLayoutChildren方法,

主要是对item进行逐一摆放,里面调用了fill方法

/**
     * {@inheritDoc}
     */
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

         //省略  测量内边距
        int startOffset;
        int endOffset;
        onAnchorReady(state, mAnchorInfo);
        detachAndScrapAttachedViews(recycler);
        mLayoutState.mIsPreLayout = state.isPreLayout();
        mLayoutState.mOnRefresLayout = true;
        if (mAnchorInfo.mLayoutFromEnd) {
            // fill towards start
            updateLayoutStateToFillStartExpose(mAnchorInfo);
            mLayoutState.mExtra = extraForStart;
            fill(recycler, mLayoutState, state, false);
            startOffset = mLayoutState.mOffset;
            if (mLayoutState.mAvailable > 0) {
                extraForEnd += mLayoutState.mAvailable;
            }
            // fill towards end
            updateLayoutStateToFillEndExpose(mAnchorInfo);
            mLayoutState.mExtra = extraForEnd;
            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
            //子view 的摆放
            fill(recycler, mLayoutState, state, false);
            endOffset = mLayoutState.mOffset;
        } else {
            // fill towards end
            updateLayoutStateToFillEndExpose(mAnchorInfo);
            mLayoutState.mExtra = extraForEnd;
            fill(recycler, mLayoutState, state, false);
            endOffset = mLayoutState.mOffset;
            if (mLayoutState.mAvailable > 0) {
                extraForStart += mLayoutState.mAvailable;
            }
            // fill towards start
            updateLayoutStateToFillStartExpose(mAnchorInfo);
            mLayoutState.mExtra = extraForStart;
            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
            fill(recycler, mLayoutState, state, false);
            startOffset = mLayoutState.mOffset;
        }

    
     //省略
        layoutForPredictiveAnimationsExpose(recycler, state, startOffset, endOffset);
        if (!state.isPreLayout()) {
            mCurrentPendingScrollPosition = RecyclerView.NO_POSITION;
            mPendingScrollPositionOffset = INVALID_OFFSET;
            mOrientationHelper.onLayoutComplete();
        }
        mLastStackFromEnd = getStackFromEnd();
        mCurrentPendingSavedState = null; // we don't need this anymore
        if (DEBUG) {
            validateChildOrderExpose();
        }
    }

ExposeLinearLayoutManagerEx#fill

/**
     * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
     * independent from the rest of the {@link LinearLayoutManager}
     * and with little change, can be made publicly available as a helper class.
     *
     * @param recycler        Current recycler that is attached to RecyclerView
     * @param layoutState     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.
     */
    protected 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;
        if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {
            // TODO ugly bug fix. should not happen
            if (layoutState.mAvailable < 0) {
                layoutState.mScrollingOffset += layoutState.mAvailable;
            }
            recycleByLayoutStateExpose(recycler, layoutState);
        }
        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
       //重点:while语句 循环对item进行摆放
        //remainingSpace 可用的空间   layoutState.hasMore item
        while (remainingSpace > 0 && layoutState.hasMore(state)) {
             layoutChunkResultCache.resetInternal();
           //进入这个方法
            layoutChunk(recycler, state, layoutState, layoutChunkResultCache);

            if (layoutChunkResultCache.mFinished) {
                break;
            }

            layoutState.mOffset += layoutChunkResultCache.mConsumed * layoutState.mLayoutDirection;
            /**
             * Consume the available space if:
             * * layoutChunk did not request to be ignored
             * * OR we are laying out scrap children
             * * OR we are not doing pre-layout
             */
            if (!layoutChunkResultCache.mIgnoreConsumed || mLayoutState.mScrapList != null
                    || !state.isPreLayout()) {
                layoutState.mAvailable -= layoutChunkResultCache.mConsumed;
                // we keep a separate remaining space because mAvailable is important for recycling
                remainingSpace -= layoutChunkResultCache.mConsumed;
            }

            if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResultCache.mConsumed;
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }
                recycleByLayoutStateExpose(recycler, layoutState);
            }
            if (stopOnFocusable && layoutChunkResultCache.mFocusable) {
                break;
            }
        }
        if (DEBUG) {
            validateChildOrderExpose();
        }
        return start - layoutState.mAvailable;
    }

继续:ExposeLinearLayoutManagerEx#layoutChunk


    protected void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
                               LayoutState layoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult result) {
         //去回收池里面取寻找view
        View view = layoutState.next(recycler);
        if (view == null) {
            if (DEBUG && layoutState.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.
            result.mFinished = true;
            return;
        }
        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
        if (layoutState.mScrapList == null) {
            // can not find in scrapList
            if (mShouldReverseLayoutExpose == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
              //添加addView
                addView(view);
            } else {
                addView(view, 0);
            }
        } else {
            if (mShouldReverseLayoutExpose == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                addDisappearingView(view);
            } else {
                addDisappearingView(view, 0);
            }
        }
        measureChildWithMargins(view, 0, 0);
        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
        int left, top, right, bottom;
        if (getOrientation() == 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);

            // whether this layout pass is layout form start to end or in reverse
            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                right = layoutState.mOffset;
                left = layoutState.mOffset - result.mConsumed;
            } else {
                left = layoutState.mOffset;
                right = layoutState.mOffset + result.mConsumed;
            }
        }
        // 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));
        }
        // Consume the available space if the view is not removed OR changed
        if (params.isItemRemoved() || params.isItemChanged()) {
            result.mIgnoreConsumed = true;
        }
        result.mFocusable = view.isFocusable();
    }

他的子类也实现了这个方法 VirtualLayoutManager#layoutChunk


    @Override
    protected void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult result) {
        final int position = layoutState.mCurrentPosition;
        mTempLayoutStateWrapper.mLayoutState = layoutState;
     //注意这个方法  主要是去寻找layoutHelper
        LayoutHelper layoutHelper = mHelperFinder == null ? null : mHelperFinder.getLayoutHelper(position);
        if (layoutHelper == null)
            layoutHelper = mDefaultLayoutHelper;
      //通过layouthelper去开始摆放布局
        layoutHelper.doLayout(recycler, state, mTempLayoutStateWrapper, result, this);
        mTempLayoutStateWrapper.mLayoutState = null;
       
//省略....
    }

LayoutHelper#doLayout方法 是抽象的

    public abstract void doLayout(RecyclerView.Recycler recycler, RecyclerView.State state,
                                  LayoutStateWrapper layoutState, LayoutChunkResult result,
                                  LayoutManagerHelper helper);

而LayoutHelper的实现类,

我们一LinearLayoutHelper为例子:LinearLayoutHelper里面没有doLayout方法,我们找到他的父类

public class LinearLayoutHelper extends BaseLayoutHelper {

BaseLayoutHelper #doLayout

  @Override
    public void doLayout(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {
        layoutViews(recycler, state, layoutState, result, helper);
    }
    public abstract void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,
                                     LayoutStateWrapper layoutState, LayoutChunkResult result,
                                     LayoutManagerHelper helper);

又是抽象方法,再次回到LinearlayoutHelper

@Override
    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,
                            VirtualLayoutManager.LayoutStateWrapper layoutState, LayoutChunkResult result,
                            LayoutManagerHelper helper) {
        // reach the end of this layout
        if (isOutOfRange(layoutState.getCurrentPosition())) {
            return;
        }
        int currentPosition = layoutState.getCurrentPosition();

        // find corresponding layout container
        View view = nextView(recycler, layoutState, helper, result);
        if (view == null) {
            return;
        }

        VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();
        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;

        int startSpace = 0, endSpace = 0, gap = 0;
        boolean isLayoutEnd = layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_END;
        boolean isStartLine = isLayoutEnd
                ? currentPosition == getRange().getLower().intValue()
                : currentPosition == getRange().getUpper().intValue();
        boolean isEndLine = isLayoutEnd
                ? currentPosition == getRange().getUpper().intValue()
                : currentPosition == getRange().getLower().intValue();

        if (isStartLine) {
            startSpace = layoutInVertical
                    ? (isLayoutEnd ? mMarginTop + mPaddingTop : mMarginBottom + mPaddingBottom)
                    : (isLayoutEnd ? mMarginLeft + mPaddingLeft : mMarginRight + mPaddingRight);
        }

        if (isEndLine) {
            endSpace = layoutInVertical
                    ? (isLayoutEnd ? mMarginBottom + mPaddingBottom : mMarginTop + mPaddingTop)
                    : (isLayoutEnd ? mMarginRight + mPaddingRight : mMarginLeft + mPaddingLeft);
        }

        if (!isStartLine) {
            gap = mDividerHeight;
        }

        final int widthSize = helper.getContentWidth() - helper.getPaddingLeft() - helper
                .getPaddingRight() - getHorizontalMargin() - getHorizontalPadding();
        int widthSpec = helper.getChildMeasureSpec(widthSize, params.width, !layoutInVertical);
        int heightSpec;
        float viewAspectRatio = params.mAspectRatio;
        if (!Float.isNaN(viewAspectRatio) && viewAspectRatio > 0) {
            heightSpec = View.MeasureSpec.makeMeasureSpec((int) (widthSize / viewAspectRatio + 0.5f),
                    View.MeasureSpec.EXACTLY);
        } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {
            heightSpec = View.MeasureSpec.makeMeasureSpec((int) (widthSize / mAspectRatio + 0.5),
                    View.MeasureSpec.EXACTLY);
        } else {
            heightSpec = helper.getChildMeasureSpec(
                    helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom()
                            - getVerticalMargin() - getVerticalPadding(), params.height,
                    layoutInVertical);
        }

        helper.measureChild(view, widthSpec, heightSpec);

        OrientationHelper orientationHelper = helper.getMainOrientationHelper();
        result.mConsumed = orientationHelper.getDecoratedMeasurement(view) + startSpace + endSpace + gap;
        int left, top, right, bottom;
        if (helper.getOrientation() == VERTICAL) {
            // not support RTL now
            if (helper.isDoLayoutRTL()) {
                right = helper.getContentWidth() - helper.getPaddingRight() - mMarginRight - mPaddingRight;
                left = right - orientationHelper.getDecoratedMeasurementInOther(view);
            } else {
                left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;
                right = left + orientationHelper.getDecoratedMeasurementInOther(view);
            }

            // whether this layout pass is layout to start or to end
            if (layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START) {
                // fill start, from bottom to top
                bottom = layoutState.getOffset() - startSpace - (isStartLine ? 0 : mDividerHeight);
                top = bottom - orientationHelper.getDecoratedMeasurement(view);
            } else {
                // fill end, from top to bottom
                top = layoutState.getOffset() + startSpace + (isStartLine ? 0 : mDividerHeight);
                bottom = top + orientationHelper.getDecoratedMeasurement(view);
            }
        } else {
            top = helper.getPaddingTop() + mMarginTop + mPaddingTop;
            bottom = top + orientationHelper.getDecoratedMeasurementInOther(view);

            if (layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START) {
                // fill left, from right to left
                right = layoutState.getOffset() - startSpace - (isStartLine ? 0 : mDividerHeight);
                left = right - orientationHelper.getDecoratedMeasurement(view);
            } else {
                // fill right, from left to right
                left = layoutState.getOffset() + startSpace + (isStartLine ? 0 : mDividerHeight);
                right = left + orientationHelper.getDecoratedMeasurement(view);
            }
        }
        // We calculate everything with View's bounding box (which includes decor and margins)
        // To calculate correct layout position, we subtract margins.
    //重点
        layoutChild(view, left, top, right, bottom, helper);



        handleStateOnResult(result, view);
    }

我们看到了真正开始布局的方法

        layoutChild(view, left, top, right, bottom, helper);

 

总结起来就是RecyclerView,RecyclerView通过VirtualLayoutManager找到合适的helper,通过helper真正开始布局view。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值