private int fixLayoutEndGap(int endOffset, Recycler recycler, State state, boolean canOffsetChildren) { int gap = this.mOrientationHelper.getEndAfterPadding() - endOffset; if (gap <= 0) { return 0; } int fixOffset = -scrollBy(-gap, recycler, state); endOffset += fixOffset; if (canOffsetChildren) { gap = this.mOrientationHelper.getEndAfterPadding() - endOffset; if (gap > 0) { this.mOrientationHelper.offsetChildren(gap); return gap + fixOffset; } } return fixOffset; }
public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfoCompat info) { android.view.ViewGroup.LayoutParams lp = host.getLayoutParams(); if (lp instanceof LayoutParams) { LayoutParams glp = (LayoutParams) lp; int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewLayoutPosition()); if (this.mOrientation == 0) { int spanIndex = glp.getSpanIndex(); int spanSize = glp.getSpanSize(); boolean z = this.mSpanCount > 1 && glp.getSpanSize() == this.mSpanCount; info.setCollectionItemInfo(CollectionItemInfoCompat.obtain(spanIndex, spanSize, spanGroupIndex, 1, z, false)); return; } int spanIndex2 = glp.getSpanIndex(); int spanSize2 = glp.getSpanSize(); boolean z2 = this.mSpanCount > 1 && glp.getSpanSize() == this.mSpanCount; info.setCollectionItemInfo(CollectionItemInfoCompat.obtain(spanGroupIndex, 1, spanIndex2, spanSize2, z2, false)); return; } super.onInitializeAccessibilityNodeInfoForItem(host, info); }
private void ensureAnchorIsInCorrectSpan(Recycler recycler, State state, AnchorInfo anchorInfo, int itemDirection) { boolean layingOutInPrimaryDirection = true; if (itemDirection != 1) { layingOutInPrimaryDirection = false; } int span = getSpanIndex(recycler, state, anchorInfo.mPosition); if (layingOutInPrimaryDirection) { while (span > 0 && anchorInfo.mPosition > 0) { anchorInfo.mPosition--; span = getSpanIndex(recycler, state, anchorInfo.mPosition); } return; } int indexLimit = state.getItemCount() - 1; int pos = anchorInfo.mPosition; int bestSpan = span; while (pos < indexLimit) { int next = getSpanIndex(recycler, state, pos + 1); if (next <= bestSpan) { break; } pos++; bestSpan = next; } anchorInfo.mPosition = pos; }
private void recycleViewsFromStart(Recycler recycler, int dt) { if (dt >= 0) { int limit = dt; int childCount = getChildCount(); int i; if (this.mShouldReverseLayout) { for (i = childCount - 1; i >= 0; i--) { if (this.mOrientationHelper.getDecoratedEnd(getChildAt(i)) > limit) { recycleChildren(recycler, childCount - 1, i); return; } } return; } for (i = 0; i < childCount; i++) { if (this.mOrientationHelper.getDecoratedEnd(getChildAt(i)) > limit) { recycleChildren(recycler, 0, i); return; } } } }
private int fixLayoutStartGap(int startOffset, Recycler recycler, State state, boolean canOffsetChildren) { int gap = startOffset - this.mOrientationHelper.getStartAfterPadding(); if (gap <= 0) { return 0; } int fixOffset = -scrollBy(gap, recycler, state); startOffset += fixOffset; if (canOffsetChildren) { gap = startOffset - this.mOrientationHelper.getStartAfterPadding(); if (gap > 0) { this.mOrientationHelper.offsetChildren(-gap); return fixOffset - gap; } } return fixOffset; }
int scrollBy(int dy, Recycler recycler, State state) { int i = 0; if (!(getChildCount() == 0 || dy == 0)) { this.mLayoutState.mRecycle = true; ensureLayoutState(); int layoutDirection = dy > 0 ? 1 : -1; int absDy = Math.abs(dy); updateLayoutState(layoutDirection, absDy, true, state); int consumed = this.mLayoutState.mScrollingOffset + fill(recycler, this.mLayoutState, state, false); if (consumed >= 0) { if (absDy > consumed) { i = layoutDirection * consumed; } else { i = dy; } this.mOrientationHelper.offsetChildren(-i); this.mLayoutState.mLastScrollDelta = i; } } return i; }
private void recycleViewsFromEnd(Recycler recycler, int dt) { int childCount = getChildCount(); if (dt >= 0) { int limit = this.mOrientationHelper.getEnd() - dt; int i; if (this.mShouldReverseLayout) { for (i = 0; i < childCount; i++) { if (this.mOrientationHelper.getDecoratedStart(getChildAt(i)) < limit) { recycleChildren(recycler, 0, i); return; } } return; } for (i = childCount - 1; i >= 0; i--) { if (this.mOrientationHelper.getDecoratedStart(getChildAt(i)) < limit) { recycleChildren(recycler, childCount - 1, i); return; } } } }
private int getSpanIndex(SpanSizeLookup spanSizeLookup, int spanCount, RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { if (!state.isPreLayout()) { return spanSizeLookup.getCachedSpanIndex(pos, spanCount); } final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); if (adapterPosition == -1) { return 0; } return spanSizeLookup.getCachedSpanIndex(adapterPosition, spanCount); }
private int getSpanSize(SpanSizeLookup spanSizeLookup, RecyclerView.Recycler recycler, RecyclerView.State state, int pos) { if (!state.isPreLayout()) { return spanSizeLookup.getSpanSize(pos); } final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); if (adapterPosition == -1) { return 0; } return spanSizeLookup.getSpanSize(adapterPosition); }
private void assignSpans(GridRangeStyle rangeStyle, RecyclerView.Recycler recycler, RecyclerView.State state, int count, int consumedSpanCount, boolean layingOutInPrimaryDirection, LayoutManagerHelper helper) { int span, spanDiff, 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; } if (helper.getOrientation() == VERTICAL && helper.isDoLayoutRTL()) { // start from last span span = consumedSpanCount - 1; spanDiff = -1; } else { span = 0; spanDiff = 1; } for (int i = start; i != end; i += diff) { View view = rangeStyle.mSet[i]; int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)); if (spanDiff == -1 && spanSize > 1) { rangeStyle.mSpanIndices[i] = span - (spanSize - 1); } else { rangeStyle.mSpanIndices[i] = span; } span += spanDiff * spanSize; } }
@Override public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) { mTempLayoutHelper = helper; doLayoutView(recycler, state, layoutState, result, helper); mTempLayoutHelper = null; }
public int getColumnCountForAccessibility(Recycler recycler, State state) { if (this.mOrientation == 1) { return this.mSpanCount; } if (state.getItemCount() < 1) { return 0; } return getSpanGroupIndex(recycler, state, state.getItemCount() - 1) + 1; }
public void onLayoutChildren(Recycler recycler, State state) { if (state.isPreLayout()) { cachePreLayoutSpanMapping(); } super.onLayoutChildren(recycler, state); clearPreLayoutSpanMappingCache(); if (!state.isPreLayout()) { this.mPendingSpanCountChange = false; } }
void onAnchorReady(Recycler recycler, State state, AnchorInfo anchorInfo, int itemDirection) { super.onAnchorReady(recycler, state, anchorInfo, itemDirection); updateMeasurements(); if (state.getItemCount() > 0 && !state.isPreLayout()) { ensureAnchorIsInCorrectSpan(recycler, state, anchorInfo, itemDirection); } ensureViewSet(); }
View next(Recycler recycler) { if (this.mScrapList != null) { return nextViewFromScrapList(); } View view = recycler.getViewForPosition(this.mCurrentPosition); this.mCurrentPosition += this.mItemDirection; return view; }
private void recycleChildren(Recycler recycler, int startIndex, int endIndex) { if (startIndex != endIndex) { int i; if (endIndex > startIndex) { for (i = endIndex - 1; i >= startIndex; i--) { removeAndRecycleViewAt(i, recycler); } return; } for (i = startIndex; i > endIndex; i--) { removeAndRecycleViewAt(i, recycler); } } }
View findReferenceChild(Recycler recycler, State state, int start, int end, int itemCount) { ensureLayoutState(); View invalidMatch = null; View outOfBoundsMatch = null; int boundsStart = this.mOrientationHelper.getStartAfterPadding(); int boundsEnd = this.mOrientationHelper.getEndAfterPadding(); int diff = end > start ? 1 : -1; for (int i = start; i != end; i += diff) { View childAt = getChildAt(i); int position = getPosition(childAt); if (position >= 0 && position < itemCount && getSpanIndex(recycler, state, position) == 0) { if (((android.support.v7.widget.RecyclerView.LayoutParams) childAt.getLayoutParams()).isItemRemoved()) { if (invalidMatch == null) { invalidMatch = childAt; } } else if (this.mOrientationHelper.getDecoratedStart(childAt) < boundsEnd && this.mOrientationHelper.getDecoratedEnd(childAt) >= boundsStart) { return childAt; } else { if (outOfBoundsMatch == null) { outOfBoundsMatch = childAt; } } } } if (outOfBoundsMatch == null) { outOfBoundsMatch = invalidMatch; } return outOfBoundsMatch; }
private int getSpanGroupIndex(Recycler recycler, State state, int viewPosition) { if (!state.isPreLayout()) { return this.mSpanSizeLookup.getSpanGroupIndex(viewPosition, this.mSpanCount); } int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition); if (adapterPosition != -1) { return this.mSpanSizeLookup.getSpanGroupIndex(adapterPosition, this.mSpanCount); } Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition); return 0; }
private int getSpanIndex(Recycler recycler, State state, int pos) { if (!state.isPreLayout()) { return this.mSpanSizeLookup.getCachedSpanIndex(pos, this.mSpanCount); } int cached = this.mPreLayoutSpanIndexCache.get(pos, -1); if (cached != -1) { return cached; } int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); if (adapterPosition != -1) { return this.mSpanSizeLookup.getCachedSpanIndex(adapterPosition, this.mSpanCount); } Log.w(TAG, "Cannot find span size for pre layout position. It is not cached, not in the adapter. Pos:" + pos); return 0; }
private int getSpanSize(Recycler recycler, State state, int pos) { if (!state.isPreLayout()) { return this.mSpanSizeLookup.getSpanSize(pos); } int cached = this.mPreLayoutSpanSizeCache.get(pos, -1); if (cached != -1) { return cached; } int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos); if (adapterPosition != -1) { return this.mSpanSizeLookup.getSpanSize(adapterPosition); } Log.w(TAG, "Cannot find span size for pre layout position. It is not cached, not in the adapter. Pos:" + pos); return 1; }
private void assignSpans(Recycler recycler, State state, int count, int consumedSpanCount, boolean layingOutInPrimaryDirection) { int start; int end; int diff; int span; int spanDiff; if (layingOutInPrimaryDirection) { start = 0; end = count; diff = 1; } else { start = count - 1; end = -1; diff = -1; } if (this.mOrientation == 1 && isLayoutRTL()) { span = this.mSpanCount - 1; spanDiff = -1; } else { span = 0; spanDiff = 1; } for (int i = start; i != end; i += diff) { View view = this.mSet[i]; LayoutParams params = (LayoutParams) view.getLayoutParams(); params.mSpanSize = getSpanSize(recycler, state, getPosition(view)); if (spanDiff != -1 || params.mSpanSize <= 1) { params.mSpanIndex = span; } else { params.mSpanIndex = span - (params.mSpanSize - 1); } span += params.mSpanSize * spanDiff; } }
void layoutChunk(Recycler recycler, State state, LayoutState layoutState, LayoutChunkResult result) { View view = layoutState.next(recycler); if (view == null) { result.mFinished = true; return; } int right; int left; int bottom; int top; LayoutParams params = (LayoutParams) view.getLayoutParams(); if (layoutState.mScrapList == null) { if (this.mShouldReverseLayout == (layoutState.mLayoutDirection == -1)) { addView(view); } else { addView(view, 0); } } else { if (this.mShouldReverseLayout == (layoutState.mLayoutDirection == -1)) { addDisappearingView(view); } else { addDisappearingView(view, 0); } } measureChildWithMargins(view, 0, 0); result.mConsumed = this.mOrientationHelper.getDecoratedMeasurement(view); if (this.mOrientation == 1) { if (isLayoutRTL()) { right = getWidth() - getPaddingRight(); left = right - this.mOrientationHelper.getDecoratedMeasurementInOther(view); } else { left = getPaddingLeft(); right = left + this.mOrientationHelper.getDecoratedMeasurementInOther(view); } if (layoutState.mLayoutDirection == -1) { bottom = layoutState.mOffset; top = layoutState.mOffset - result.mConsumed; } else { top = layoutState.mOffset; bottom = layoutState.mOffset + result.mConsumed; } } else { top = getPaddingTop(); bottom = top + this.mOrientationHelper.getDecoratedMeasurementInOther(view); if (layoutState.mLayoutDirection == -1) { right = layoutState.mOffset; left = layoutState.mOffset - result.mConsumed; } else { left = layoutState.mOffset; right = layoutState.mOffset + result.mConsumed; } } layoutDecorated(view, left + params.leftMargin, top + params.topMargin, right - params.rightMargin, bottom - params.bottomMargin); if (params.isItemRemoved() || params.isItemChanged()) { result.mIgnoreConsumed = true; } result.mFocusable = view.isFocusable(); }
View findReferenceChild(Recycler recycler, State state, int start, int end, int itemCount) { ensureLayoutState(); View invalidMatch = null; View outOfBoundsMatch = null; int boundsStart = this.mOrientationHelper.getStartAfterPadding(); int boundsEnd = this.mOrientationHelper.getEndAfterPadding(); int diff = end > start ? 1 : -1; for (int i = start; i != end; i += diff) { View childAt = getChildAt(i); int position = getPosition(childAt); if (position >= 0 && position < itemCount) { if (((LayoutParams) childAt.getLayoutParams()).isItemRemoved()) { if (invalidMatch == null) { invalidMatch = childAt; } } else if (this.mOrientationHelper.getDecoratedStart(childAt) < boundsEnd && this.mOrientationHelper.getDecoratedEnd(childAt) >= boundsStart) { return childAt; } else { if (outOfBoundsMatch == null) { outOfBoundsMatch = childAt; } } } } if (outOfBoundsMatch == null) { outOfBoundsMatch = invalidMatch; } return outOfBoundsMatch; }
public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state, View host, AccessibilityNodeInfoCompat info) { android.view.ViewGroup.LayoutParams lp = host.getLayoutParams(); if (lp instanceof LayoutParams) { LayoutParams sglp = (LayoutParams) lp; if (this.mOrientation == 0) { info.setCollectionItemInfo(CollectionItemInfoCompat.obtain(sglp.getSpanIndex(), sglp.mFullSpan ? this.mSpanCount : 1, -1, -1, sglp.mFullSpan, false)); return; } else { info.setCollectionItemInfo(CollectionItemInfoCompat.obtain(-1, -1, sglp.getSpanIndex(), sglp.mFullSpan ? this.mSpanCount : 1, sglp.mFullSpan, false)); return; } } super.onInitializeAccessibilityNodeInfoForItem(host, info); }
private void recycle(Recycler recycler, LayoutState layoutState) { if (layoutState.mRecycle && !layoutState.mInfinite) { if (layoutState.mAvailable == 0) { if (layoutState.mLayoutDirection == -1) { recycleFromEnd(recycler, layoutState.mEndLine); } else { recycleFromStart(recycler, layoutState.mStartLine); } } else if (layoutState.mLayoutDirection == -1) { scrolled = layoutState.mStartLine - getMaxStart(layoutState.mStartLine); if (scrolled < 0) { line = layoutState.mEndLine; } else { line = layoutState.mEndLine - Math.min(scrolled, layoutState.mAvailable); } recycleFromEnd(recycler, line); } else { scrolled = getMinEnd(layoutState.mEndLine) - layoutState.mEndLine; if (scrolled < 0) { line = layoutState.mStartLine; } else { line = layoutState.mStartLine + Math.min(scrolled, layoutState.mAvailable); } recycleFromStart(recycler, line); } } }
private void recycleFromStart(Recycler recycler, int line) { while (getChildCount() > 0) { View child = getChildAt(0); if (this.mPrimaryOrientation.getDecoratedEnd(child) <= line) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.mFullSpan) { int j = 0; while (j < this.mSpanCount) { if (this.mSpans[j].mViews.size() != 1) { j++; } else { return; } } for (j = 0; j < this.mSpanCount; j++) { this.mSpans[j].popStart(); } } else if (lp.mSpan.mViews.size() != 1) { lp.mSpan.popStart(); } else { return; } removeAndRecycleView(child, recycler); } else { return; } } }
private void recycleFromEnd(Recycler recycler, int line) { int i = getChildCount() - 1; while (i >= 0) { View child = getChildAt(i); if (this.mPrimaryOrientation.getDecoratedStart(child) >= line) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.mFullSpan) { int j = 0; while (j < this.mSpanCount) { if (this.mSpans[j].mViews.size() != 1) { j++; } else { return; } } for (j = 0; j < this.mSpanCount; j++) { this.mSpans[j].popEnd(); } } else if (lp.mSpan.mViews.size() != 1) { lp.mSpan.popEnd(); } else { return; } removeAndRecycleView(child, recycler); i--; } else { return; } } }
int scrollBy(int dt, Recycler recycler, State state) { int layoutDir; int referenceChildPosition; int totalScroll; ensureOrientationHelper(); if (dt > 0) { layoutDir = 1; referenceChildPosition = getLastChildPosition(); } else { layoutDir = -1; referenceChildPosition = getFirstChildPosition(); } this.mLayoutState.mRecycle = true; updateLayoutState(referenceChildPosition, state); setLayoutStateDirection(layoutDir); this.mLayoutState.mCurrentPosition = this.mLayoutState.mItemDirection + referenceChildPosition; int absDt = Math.abs(dt); this.mLayoutState.mAvailable = absDt; int consumed = fill(recycler, this.mLayoutState, state); if (absDt < consumed) { totalScroll = dt; } else if (dt < 0) { totalScroll = -consumed; } else { totalScroll = consumed; } this.mPrimaryOrientation.offsetChildren(-totalScroll); this.mLastLayoutFromEnd = this.mShouldReverseLayout; return totalScroll; }
public void onDetachedFromWindow(RecyclerView view, Recycler recycler) { super.onDetachedFromWindow(view, recycler); if (this.mRecycleChildrenOnDetach) { removeAndRecycleAllViews(recycler); recycler.clear(); } }
int fill(Recycler recycler, LayoutState layoutState, State state, boolean stopOnFocusable) { int start = layoutState.mAvailable; if (layoutState.mScrollingOffset != Integer.MIN_VALUE) { if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } recycleByLayoutState(recycler, layoutState); } int remainingSpace = layoutState.mAvailable + layoutState.mExtra; LayoutChunkResult layoutChunkResult = new LayoutChunkResult(); while (true) { if ((!layoutState.mInfinite && remainingSpace <= 0) || !layoutState.hasMore(state)) { break; } layoutChunkResult.resetInternal(); layoutChunk(recycler, state, layoutState, layoutChunkResult); if (!layoutChunkResult.mFinished) { layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection; if (!(layoutChunkResult.mIgnoreConsumed && this.mLayoutState.mScrapList == null && state.isPreLayout())) { layoutState.mAvailable -= layoutChunkResult.mConsumed; remainingSpace -= layoutChunkResult.mConsumed; } if (layoutState.mScrollingOffset != Integer.MIN_VALUE) { layoutState.mScrollingOffset += layoutChunkResult.mConsumed; if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } recycleByLayoutState(recycler, layoutState); } if (stopOnFocusable && layoutChunkResult.mFocusable) { break; } } else { break; } } return start - layoutState.mAvailable; }
private void layoutForPredictiveAnimations(Recycler recycler, State state, int startOffset, int endOffset) { if (state.willRunPredictiveAnimations() && getChildCount() != 0 && !state.isPreLayout() && supportsPredictiveItemAnimations()) { int scrapExtraStart = 0; int scrapExtraEnd = 0; List<ViewHolder> scrapList = recycler.getScrapList(); int scrapSize = scrapList.size(); int firstChildPos = getPosition(getChildAt(0)); for (int i = 0; i < scrapSize; i++) { ViewHolder scrap = (ViewHolder) scrapList.get(i); if (!scrap.isRemoved()) { if (((scrap.getLayoutPosition() < firstChildPos) != this.mShouldReverseLayout ? -1 : 1) == -1) { scrapExtraStart += this.mOrientationHelper.getDecoratedMeasurement(scrap.itemView); } else { scrapExtraEnd += this.mOrientationHelper.getDecoratedMeasurement(scrap.itemView); } } } this.mLayoutState.mScrapList = scrapList; if (scrapExtraStart > 0) { updateLayoutStateToFillStart(getPosition(getChildClosestToStart()), startOffset); this.mLayoutState.mExtra = scrapExtraStart; this.mLayoutState.mAvailable = 0; this.mLayoutState.assignPositionFromScrapList(); fill(recycler, this.mLayoutState, state, false); } if (scrapExtraEnd > 0) { updateLayoutStateToFillEnd(getPosition(getChildClosestToEnd()), endOffset); this.mLayoutState.mExtra = scrapExtraEnd; this.mLayoutState.mAvailable = 0; this.mLayoutState.assignPositionFromScrapList(); fill(recycler, this.mLayoutState, state, false); } this.mLayoutState.mScrapList = null; } }