Android 수필 - GridLayoutManager
14519 단어 안 드 로 이 드 에 세이
여 기 는 RecyclerView 그리 기 절차 에 대한 상세 한 설명 입 니 다. http://blog.csdn.net/hfyd_/article/details/53910631
배경:
다음 설명 은 RecyclerView 에 GridLayoutManager 가 설 치 된 상황 에서 방향 은 vertical 입 니 다.
공정 onMeasure -- onLayout -- ondraw (fill)
onlayoutchild(), , mLayout.onLayoutChildren(mRecycler, mState); mlayout setLayoutManager() , 。
GridLayout Manager 에서 이 방법 을 실 현 했 습 니 다.
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (state.isPreLayout()) { cachePreLayoutSpanMapping(); } super.onLayoutChildren(recycler, state); if (DEBUG) { validateChildOrder(); } clearPreLayoutSpanMappingCache(); }
우 리 는 슈퍼. onlayot Children () 을 호출 하 는 것 을 보 았 다.그래서 계속 들 어가 보 겠 습 니 다.
)LinearLayoutManager , fill(); fill() :
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.SCROLLING_OFFSET_NaN) { // TODO ugly bug fix. should not happen if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } recycleByLayoutState(recycler, layoutState); } int remainingSpace = layoutState.mAvailable + layoutState.mExtra; LayoutChunkResult layoutChunkResult = mLayoutChunkResult; while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { layoutChunkResult.resetInternal(); layoutChunk(recycler, state, layoutState, layoutChunkResult); if (layoutChunkResult.mFinished) { break; }
while 에서 layoutChunk () 를 호출 하여 하위 view 를 측정 하고 레이아웃 하 며 그립 니 다.그 러 니까 이게 내 가 중점적으로 말 하고 자 하 는 부분 이 야.while 에서 remaining Space 가 0 보다 큰 지 판단 합 니 다. 이것 은 판단 서브 view 가 필요 하 다 고 생각 합 니 다., layoutChunk() RecyclerView ( vertical). layoutChunk()
GridLayoutManager :
if (!layingOutInPrimaryDirection) { int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition); int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition); remainingSpan = itemSpanIndex + itemSpanSize; } while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { int pos = layoutState.mCurrentPosition; final int spanSize = getSpanSize(recycler, state, pos); if (spanSize > mSpanCount) { throw new IllegalArgumentException("Item at position " + pos + " requires " + spanSize + " spans but GridLayoutManager has only " + mSpanCount + " spans."); } remainingSpan -= spanSize; if (remainingSpan < 0) { break; // item did not fit into this row or column } View view = layoutState.next(recycler); if (view == null) { break; } consumedSpanCount += spanSize; mSet[count] = view; count++; }
이 코드 는 각 줄 (즉 chunk) 에 itemview 를 분배 합 니 다.:
assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection); for (int i = 0; i < count; i++) { View view = mSet[i]; if (layoutState.mScrapList == null) { if (layingOutInPrimaryDirection) { addView(view); } else { addView(view, 0); } } else { if (layingOutInPrimaryDirection) { addDisappearingView(view); } else { addDisappearingView(view, 0); } }
calculateItemDecorationsForChild(view, mDecorInsets); measureChild(view, otherDirSpecMode, false); final int size = mOrientationHelper.getDecoratedMeasurement(view); if (size > maxSize) { maxSize = size; } final LayoutParams lp = (LayoutParams) view.getLayoutParams(); final float otherSize = 1f * mOrientationHelper.getDecoratedMeasurementInOther(view) / lp.mSpanSize; if (otherSize > maxSizeInOther) { maxSizeInOther = otherSize; }
assignSpans()
itemview ( itemview ) ( itemview , , 3, 0, 1);, itemview RecyclerView 。
itemview offset, getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state),calculateItemDecorationsForChild(view, mDecorInsets);
RecyclerView.ItemDecoration , , itemview
measureChild :
private void measureChild(View view, int otherDirParentSpecMode, boolean alreadyMeasured) { final LayoutParams lp = (LayoutParams) view.getLayoutParams(); final Rect decorInsets = lp.mDecorInsets; final int verticalInsets = decorInsets.top + decorInsets.bottom + lp.topMargin + lp.bottomMargin; final int horizontalInsets = decorInsets.left + decorInsets.right + lp.leftMargin + lp.rightMargin; final int availableSpaceInOther = getSpaceForSpanRange(lp.mSpanIndex, lp.mSpanSize); final int wSpec; final int hSpec; if (mOrientation == VERTICAL) { wSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, horizontalInsets, lp.width, false); hSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getHeightMode(), verticalInsets, lp.height, true); } else { hSpec = getChildMeasureSpec(availableSpaceInOther, otherDirParentSpecMode, verticalInsets, lp.height, false); wSpec = getChildMeasureSpec(mOrientationHelper.getTotalSpace(), getWidthMode(), horizontalInsets, lp.width, true); } measureChildWithDecorationsAndMargin(view, wSpec, hSpec, alreadyMeasured); }
private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec, boolean alreadyMeasured) { RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams(); final boolean measure; if (alreadyMeasured) { measure = shouldReMeasureChild(child, widthSpec, heightSpec, lp); } else { measure = shouldMeasureChild(child, widthSpec, heightSpec, lp); } if (measure) { child.measure(widthSpec, heightSpec); } }
measureChild() itemview ,availableSpaceInOther itemview , getChildMeasureSpec , itemview
width height, ,return MeasureSpec.makeMeasureSpec(resultSize, resultMode); measureChildWithDecorationsAndMargin
itemview width height 。
itemview , itemviewwidth height, , itemview left,top,right,bottom
int left = 0, right = 0, top = 0, bottom = 0; if (mOrientation == VERTICAL) {
// top,bottom if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { bottom = layoutState.mOffset; top = bottom - maxSize;//maxSize itemview itemview } else { top = layoutState.mOffset; bottom = top + maxSize; } } else { if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { right = layoutState.mOffset; left = right - maxSize; } else { left = layoutState.mOffset; right = left + maxSize; } } for (int i = 0; i < count; i++) { View view = mSet[i]; LayoutParams params = (LayoutParams) view.getLayoutParams(); if (mOrientation == VERTICAL) { if (isLayoutRTL()) { right = getPaddingLeft() + mCachedBorders[mSpanCount - params.mSpanIndex]; left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); } else { left = getPaddingLeft() + mCachedBorders[params.mSpanIndex]; right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); } } else { top = getPaddingTop() + mCachedBorders[params.mSpanIndex]; bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); } // We calculate everything with View's bounding box (which includes decor and margins) // To calculate correct layout position, we subtract margins.
top bottom , right left, mCachedBorders[mSpanCount - params.mSpanIndex], view ,mSpanCount ,
params.mSpanIndex itemview , assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
int consumedSpanCount, boolean layingOutInPrimaryDirection) , :
......
private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count, int consumedSpanCount, boolean layingOutInPrimaryDirection) { // spans are always assigned from 0 to N no matter if it is RTL or not. // RTL is used only when positioning the view. int span, start, end, diff; // make sure we traverse from min position to max position if (layingOutInPrimaryDirection) { start = 0; end = count; diff = 1; } else { start = count - 1; end = -1; diff = -1; } span = 0; for (int i = start; i != end; i += diff) { View view = mSet[i]; LayoutParams params = (LayoutParams) view.getLayoutParams(); params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); params.mSpanIndex = span; span += params.mSpanSize; } }
.......
final boolean layingOutInPrimaryDirection =layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; mLayoutState.mItemDirection =mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
layingOutInPrimaryDirection, mShouldReverseLayout ,mShouldReverseLayout : LayoutManager view,( from end to start
from start to end). layingOutInPrimaryDirection params.mSpanIndexmCachedBorders[] :
......
static int[] calculateItemBorders(int[] cachedBorders, int spanCount, int totalSpace) { if (cachedBorders == null || cachedBorders.length != spanCount + 1 || cachedBorders[cachedBorders.length - 1] != totalSpace) { cachedBorders = new int[spanCount + 1]; } cachedBorders[0] = 0; int sizePerSpan = totalSpace / spanCount; int sizePerSpanRemainder = totalSpace % spanCount; int consumedPixels = 0; int additionalSize = 0; for (int i = 1; i <= spanCount; i++) { int itemSize = sizePerSpan; additionalSize += sizePerSpanRemainder; if (additionalSize > 0 && (spanCount - additionalSize) < sizePerSpanRemainder) { itemSize += 1; additionalSize -= spanCount; } consumedPixels += itemSize; cachedBorders[i] = consumedPixels; } return cachedBorders; }
......
:mCachedBorders[] +1 , itemview , i i*itemsizeitemview ,
...... layoutDecoratedWithMargins(view, left, top, right, bottom); ......
。layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) ,
。