VLayout 概念
定义:
VLayout全程VirtuaLayout,它是一个针对RecyclerView的LayoutManager扩展,主要提供一整套布局方案
和布局间的组件复用的问题。由阿里团队开发,已经运用在手机淘宝项目上。2017年3月份开源。它通过定制化的LayoutManager,
接管整个RecyclerView布局逻辑。
使用:
implementation 'com.alibaba.android:vlayout:1.0.3'
自己创建一个Adapter
public class BaseDelegeteAdapter extends DelegateAdapter.Adapter{
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 extends RecyclerView.Adapter {
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。