Methods Summary |
---|
private void | appendViewToAllSpans(android.view.View view)
// traverse in reverse so that we end up assigning full span items to 0
for (int i = mSpanCount - 1; i >= 0; i--) {
mSpans[i].appendToSpan(view);
}
|
private void | applyPendingSavedState(android.support.v7.widget.StaggeredGridLayoutManager$AnchorInfo anchorInfo)
if (DEBUG) {
Log.d(TAG, "found saved state: " + mPendingSavedState);
}
if (mPendingSavedState.mSpanOffsetsSize > 0) {
if (mPendingSavedState.mSpanOffsetsSize == mSpanCount) {
for (int i = 0; i < mSpanCount; i++) {
mSpans[i].clear();
int line = mPendingSavedState.mSpanOffsets[i];
if (line != Span.INVALID_LINE) {
if (mPendingSavedState.mAnchorLayoutFromEnd) {
line += mPrimaryOrientation.getEndAfterPadding();
} else {
line += mPrimaryOrientation.getStartAfterPadding();
}
}
mSpans[i].setLine(line);
}
} else {
mPendingSavedState.invalidateSpanInfo();
mPendingSavedState.mAnchorPosition = mPendingSavedState.mVisibleAnchorPosition;
}
}
mLastLayoutRTL = mPendingSavedState.mLastLayoutRTL;
setReverseLayout(mPendingSavedState.mReverseLayout);
resolveShouldLayoutReverse();
if (mPendingSavedState.mAnchorPosition != NO_POSITION) {
mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
} else {
anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
}
if (mPendingSavedState.mSpanLookupSize > 1) {
mLazySpanLookup.mData = mPendingSavedState.mSpanLookup;
mLazySpanLookup.mFullSpanItems = mPendingSavedState.mFullSpanItems;
}
|
boolean | areAllEndsEqual()
int end = mSpans[0].getEndLine(Span.INVALID_LINE);
for (int i = 1; i < mSpanCount; i++) {
if (mSpans[i].getEndLine(Span.INVALID_LINE) != end) {
return false;
}
}
return true;
|
boolean | areAllStartsEqual()
int start = mSpans[0].getStartLine(Span.INVALID_LINE);
for (int i = 1; i < mSpanCount; i++) {
if (mSpans[i].getStartLine(Span.INVALID_LINE) != start) {
return false;
}
}
return true;
|
public void | assertNotInLayoutOrScroll(java.lang.String message)
if (mPendingSavedState == null) {
super.assertNotInLayoutOrScroll(message);
}
|
private void | attachViewToSpans(android.view.View view, android.support.v7.widget.StaggeredGridLayoutManager$LayoutParams lp, LayoutState layoutState)
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_END) {
if (lp.mFullSpan) {
appendViewToAllSpans(view);
} else {
lp.mSpan.appendToSpan(view);
}
} else {
if (lp.mFullSpan) {
prependViewToAllSpans(view);
} else {
lp.mSpan.prependToSpan(view);
}
}
|
private int | calculateScrollDirectionForPosition(int position)
if (getChildCount() == 0) {
return mShouldReverseLayout ? LAYOUT_END : LAYOUT_START;
}
final int firstChildPos = getFirstChildPosition();
return position < firstChildPos != mShouldReverseLayout ? LAYOUT_START : LAYOUT_END;
|
public boolean | canScrollHorizontally()
return mOrientation == HORIZONTAL;
|
public boolean | canScrollVertically()
return mOrientation == VERTICAL;
|
private void | checkForGaps()Checks for gaps in the UI that may be caused by adapter changes.
When a full span item is laid out in reverse direction, it sets a flag which we check when
scroll is stopped (or re-layout happens) and re-layout after first valid item.
if (getChildCount() == 0 || mGapStrategy == GAP_HANDLING_NONE) {
return;
}
final int minPos, maxPos;
if (mShouldReverseLayout) {
minPos = getLastChildPosition();
maxPos = getFirstChildPosition();
} else {
minPos = getFirstChildPosition();
maxPos = getLastChildPosition();
}
if (minPos == 0) {
View gapView = hasGapsToFix();
if (gapView != null) {
mLazySpanLookup.clear();
requestSimpleAnimationsInNextLayout();
requestLayout();
return;
}
}
if (!mLaidOutInvalidFullSpan) {
return;
}
int invalidGapDir = mShouldReverseLayout ? LAYOUT_START : LAYOUT_END;
final LazySpanLookup.FullSpanItem invalidFsi = mLazySpanLookup
.getFirstFullSpanItemInRange(minPos, maxPos + 1, invalidGapDir, true);
if (invalidFsi == null) {
mLaidOutInvalidFullSpan = false;
mLazySpanLookup.forceInvalidateAfter(maxPos + 1);
return;
}
final LazySpanLookup.FullSpanItem validFsi = mLazySpanLookup
.getFirstFullSpanItemInRange(minPos, invalidFsi.mPosition,
invalidGapDir * -1, true);
if (validFsi == null) {
mLazySpanLookup.forceInvalidateAfter(invalidFsi.mPosition);
} else {
mLazySpanLookup.forceInvalidateAfter(validFsi.mPosition + 1);
}
requestSimpleAnimationsInNextLayout();
requestLayout();
|
public boolean | checkLayoutParams(RecyclerView.LayoutParams lp)
return lp instanceof LayoutParams;
|
private boolean | checkSpanForGap(android.support.v7.widget.StaggeredGridLayoutManager$Span span)
if (mShouldReverseLayout) {
if (span.getEndLine() < mPrimaryOrientation.getEndAfterPadding()) {
return true;
}
} else if (span.getStartLine() > mPrimaryOrientation.getStartAfterPadding()) {
return true;
}
return false;
|
public int | computeHorizontalScrollExtent(RecyclerView.State state)
return computeScrollExtent(state);
|
public int | computeHorizontalScrollOffset(RecyclerView.State state)
return computeScrollOffset(state);
|
public int | computeHorizontalScrollRange(RecyclerView.State state)
return computeScrollRange(state);
|
private int | computeScrollExtent(RecyclerView.State state)
if (getChildCount() == 0) {
return 0;
}
ensureOrientationHelper();
return ScrollbarHelper.computeScrollExtent(state, mPrimaryOrientation,
findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true)
, findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled);
|
private int | computeScrollOffset(RecyclerView.State state)
if (getChildCount() == 0) {
return 0;
}
ensureOrientationHelper();
return ScrollbarHelper.computeScrollOffset(state, mPrimaryOrientation,
findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true)
, findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled, mShouldReverseLayout);
|
private int | computeScrollRange(RecyclerView.State state)
if (getChildCount() == 0) {
return 0;
}
ensureOrientationHelper();
return ScrollbarHelper.computeScrollRange(state, mPrimaryOrientation,
findFirstVisibleItemClosestToStart(!mSmoothScrollbarEnabled, true)
, findFirstVisibleItemClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled);
|
public int | computeVerticalScrollExtent(RecyclerView.State state)
return computeScrollExtent(state);
|
public int | computeVerticalScrollOffset(RecyclerView.State state)
return computeScrollOffset(state);
|
public int | computeVerticalScrollRange(RecyclerView.State state)
return computeScrollRange(state);
|
private LazySpanLookup.FullSpanItem | createFullSpanItemFromEnd(int newItemTop)
LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem();
fsi.mGapPerSpan = new int[mSpanCount];
for (int i = 0; i < mSpanCount; i++) {
fsi.mGapPerSpan[i] = newItemTop - mSpans[i].getEndLine(newItemTop);
}
return fsi;
|
private LazySpanLookup.FullSpanItem | createFullSpanItemFromStart(int newItemBottom)
LazySpanLookup.FullSpanItem fsi = new LazySpanLookup.FullSpanItem();
fsi.mGapPerSpan = new int[mSpanCount];
for (int i = 0; i < mSpanCount; i++) {
fsi.mGapPerSpan[i] = mSpans[i].getStartLine(newItemBottom) - newItemBottom;
}
return fsi;
|
private void | ensureOrientationHelper()
if (mPrimaryOrientation == null) {
mPrimaryOrientation = OrientationHelper.createOrientationHelper(this, mOrientation);
mSecondaryOrientation = OrientationHelper
.createOrientationHelper(this, 1 - mOrientation);
mLayoutState = new LayoutState();
}
|
private int | fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state)
mRemainingSpans.set(0, mSpanCount, true);
// The target position we are trying to reach.
final int targetLine;
/*
* The line until which we can recycle, as long as we add views.
* Keep in mind, it is still the line in layout direction which means; to calculate the
* actual recycle line, we should subtract/add the size in orientation.
*/
final int recycleLine;
// Line of the furthest row.
if (layoutState.mLayoutDirection == LAYOUT_END) {
// ignore padding for recycler
recycleLine = mPrimaryOrientation.getEndAfterPadding() + mLayoutState.mAvailable;
targetLine = recycleLine + mLayoutState.mExtra + mPrimaryOrientation.getEndPadding();
} else { // LAYOUT_START
// ignore padding for recycler
recycleLine = mPrimaryOrientation.getStartAfterPadding() - mLayoutState.mAvailable;
targetLine = recycleLine - mLayoutState.mExtra -
mPrimaryOrientation.getStartAfterPadding();
}
updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
// the default coordinate to add new view.
final int defaultNewViewLine = mShouldReverseLayout
? mPrimaryOrientation.getEndAfterPadding()
: mPrimaryOrientation.getStartAfterPadding();
while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) {
View view = layoutState.next(recycler);
LayoutParams lp = ((LayoutParams) view.getLayoutParams());
final int position = lp.getViewLayoutPosition();
final int spanIndex = mLazySpanLookup.getSpan(position);
Span currentSpan;
final boolean assignSpan = spanIndex == LayoutParams.INVALID_SPAN_ID;
if (assignSpan) {
currentSpan = lp.mFullSpan ? mSpans[0] : getNextSpan(layoutState);
mLazySpanLookup.setSpan(position, currentSpan);
if (DEBUG) {
Log.d(TAG, "assigned " + currentSpan.mIndex + " for " + position);
}
} else {
if (DEBUG) {
Log.d(TAG, "using " + spanIndex + " for pos " + position);
}
currentSpan = mSpans[spanIndex];
}
// assign span before measuring so that item decorators can get updated span index
lp.mSpan = currentSpan;
if (layoutState.mLayoutDirection == LAYOUT_END) {
addView(view);
} else {
addView(view, 0);
}
measureChildWithDecorationsAndMargin(view, lp);
final int start;
final int end;
if (layoutState.mLayoutDirection == LAYOUT_END) {
start = lp.mFullSpan ? getMaxEnd(defaultNewViewLine)
: currentSpan.getEndLine(defaultNewViewLine);
end = start + mPrimaryOrientation.getDecoratedMeasurement(view);
if (assignSpan && lp.mFullSpan) {
LazySpanLookup.FullSpanItem fullSpanItem;
fullSpanItem = createFullSpanItemFromEnd(start);
fullSpanItem.mGapDir = LAYOUT_START;
fullSpanItem.mPosition = position;
mLazySpanLookup.addFullSpanItem(fullSpanItem);
}
} else {
end = lp.mFullSpan ? getMinStart(defaultNewViewLine)
: currentSpan.getStartLine(defaultNewViewLine);
start = end - mPrimaryOrientation.getDecoratedMeasurement(view);
if (assignSpan && lp.mFullSpan) {
LazySpanLookup.FullSpanItem fullSpanItem;
fullSpanItem = createFullSpanItemFromStart(end);
fullSpanItem.mGapDir = LAYOUT_END;
fullSpanItem.mPosition = position;
mLazySpanLookup.addFullSpanItem(fullSpanItem);
}
}
// check if this item may create gaps in the future
if (lp.mFullSpan && layoutState.mItemDirection == ITEM_DIRECTION_HEAD) {
if (assignSpan) {
mLaidOutInvalidFullSpan = true;
} else {
final boolean hasInvalidGap;
if (layoutState.mLayoutDirection == LAYOUT_END) {
hasInvalidGap = !areAllEndsEqual();
} else { // layoutState.mLayoutDirection == LAYOUT_START
hasInvalidGap = !areAllStartsEqual();
}
if (hasInvalidGap) {
final LazySpanLookup.FullSpanItem fullSpanItem = mLazySpanLookup
.getFullSpanItem(position);
if (fullSpanItem != null) {
fullSpanItem.mHasUnwantedGapAfter = true;
}
mLaidOutInvalidFullSpan = true;
}
}
}
attachViewToSpans(view, lp, layoutState);
final int otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding()
: currentSpan.mIndex * mSizePerSpan +
mSecondaryOrientation.getStartAfterPadding();
final int otherEnd = otherStart + mSecondaryOrientation.getDecoratedMeasurement(view);
if (mOrientation == VERTICAL) {
layoutDecoratedWithMargins(view, otherStart, start, otherEnd, end);
} else {
layoutDecoratedWithMargins(view, start, otherStart, end, otherEnd);
}
if (lp.mFullSpan) {
updateAllRemainingSpans(mLayoutState.mLayoutDirection, targetLine);
} else {
updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
}
recycle(recycler, mLayoutState, currentSpan, recycleLine);
}
if (DEBUG) {
Log.d(TAG, "fill, " + getChildCount());
}
if (mLayoutState.mLayoutDirection == LAYOUT_START) {
final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding());
return Math.max(0, mLayoutState.mAvailable + (recycleLine - minStart));
} else {
final int max = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
return Math.max(0, mLayoutState.mAvailable + (max - recycleLine));
}
|
public int[] | findFirstCompletelyVisibleItemPositions(int[] into)Returns the adapter position of the first completely visible view for each span.
Note that, this value is not affected by layout orientation or item order traversal.
({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
not in the layout.
If RecyclerView has item decorators, they will be considered in calculations as well.
StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
views are ignored in this method.
if (into == null) {
into = new int[mSpanCount];
} else if (into.length < mSpanCount) {
throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
+ " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
}
for (int i = 0; i < mSpanCount; i++) {
into[i] = mSpans[i].findFirstCompletelyVisibleItemPosition();
}
return into;
|
private int | findFirstReferenceChildPosition(int itemCount)Finds the first View that can be used as an anchor View.
final int limit = getChildCount();
for (int i = 0; i < limit; i++) {
final View view = getChildAt(i);
final int position = getPosition(view);
if (position >= 0 && position < itemCount) {
return position;
}
}
return 0;
|
android.view.View | findFirstVisibleItemClosestToEnd(boolean fullyVisible, boolean acceptPartiallyVisible)
ensureOrientationHelper();
final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
final int boundsEnd = mPrimaryOrientation.getEndAfterPadding();
View partiallyVisible = null;
for (int i = getChildCount() - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (mPrimaryOrientation.getDecoratedStart(child) >= boundsStart) {
if (!fullyVisible || mPrimaryOrientation.getDecoratedEnd(child) <= boundsEnd) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
}
}
return partiallyVisible;
|
android.view.View | findFirstVisibleItemClosestToStart(boolean fullyVisible, boolean acceptPartiallyVisible)
ensureOrientationHelper();
final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
final int boundsEnd = mPrimaryOrientation.getEndAfterPadding();
final int limit = getChildCount();
View partiallyVisible = null;
for (int i = 0; i < limit; i++) {
final View child = getChildAt(i);
if (mPrimaryOrientation.getDecoratedEnd(child) <= boundsEnd) {
if ((!fullyVisible
|| mPrimaryOrientation.getDecoratedStart(child) >= boundsStart)) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
}
}
return partiallyVisible;
|
int | findFirstVisibleItemPositionInt()Finds the first fully visible child to be used as an anchor child if span count changes when
state is restored. If no children is fully visible, returns a partially visible child instead
of returning null.
final View first = mShouldReverseLayout ? findFirstVisibleItemClosestToEnd(true, true) :
findFirstVisibleItemClosestToStart(true, true);
return first == null ? NO_POSITION : getPosition(first);
|
public int[] | findFirstVisibleItemPositions(int[] into)Returns the adapter position of the first visible view for each span.
Note that, this value is not affected by layout orientation or item order traversal.
({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
not in the layout.
If RecyclerView has item decorators, they will be considered in calculations as well.
StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
views are ignored in this method.
if (into == null) {
into = new int[mSpanCount];
} else if (into.length < mSpanCount) {
throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
+ " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
}
for (int i = 0; i < mSpanCount; i++) {
into[i] = mSpans[i].findFirstVisibleItemPosition();
}
return into;
|
public int[] | findLastCompletelyVisibleItemPositions(int[] into)Returns the adapter position of the last completely visible view for each span.
Note that, this value is not affected by layout orientation or item order traversal.
({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
not in the layout.
If RecyclerView has item decorators, they will be considered in calculations as well.
StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
views are ignored in this method.
if (into == null) {
into = new int[mSpanCount];
} else if (into.length < mSpanCount) {
throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
+ " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
}
for (int i = 0; i < mSpanCount; i++) {
into[i] = mSpans[i].findLastCompletelyVisibleItemPosition();
}
return into;
|
private int | findLastReferenceChildPosition(int itemCount)Finds the last View that can be used as an anchor View.
for (int i = getChildCount() - 1; i >= 0; i--) {
final View view = getChildAt(i);
final int position = getPosition(view);
if (position >= 0 && position < itemCount) {
return position;
}
}
return 0;
|
public int[] | findLastVisibleItemPositions(int[] into)Returns the adapter position of the last visible view for each span.
Note that, this value is not affected by layout orientation or item order traversal.
({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
not in the layout.
If RecyclerView has item decorators, they will be considered in calculations as well.
StaggeredGridLayoutManager may pre-cache some views that are not necessarily visible. Those
views are ignored in this method.
if (into == null) {
into = new int[mSpanCount];
} else if (into.length < mSpanCount) {
throw new IllegalArgumentException("Provided int[]'s size must be more than or equal"
+ " to span count. Expected:" + mSpanCount + ", array size:" + into.length);
}
for (int i = 0; i < mSpanCount; i++) {
into[i] = mSpans[i].findLastVisibleItemPosition();
}
return into;
|
private void | fixEndGap(RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren)
final int maxEndLine = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
int gap = mPrimaryOrientation.getEndAfterPadding() - maxEndLine;
int fixOffset;
if (gap > 0) {
fixOffset = -scrollBy(-gap, recycler, state);
} else {
return; // nothing to fix
}
gap -= fixOffset;
if (canOffsetChildren && gap > 0) {
mPrimaryOrientation.offsetChildren(gap);
}
|
private void | fixStartGap(RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren)
final int minStartLine = getMinStart(mPrimaryOrientation.getStartAfterPadding());
int gap = minStartLine - mPrimaryOrientation.getStartAfterPadding();
int fixOffset;
if (gap > 0) {
fixOffset = scrollBy(gap, recycler, state);
} else {
return; // nothing to fix
}
gap -= fixOffset;
if (canOffsetChildren && gap > 0) {
mPrimaryOrientation.offsetChildren(-gap);
}
|
public RecyclerView.LayoutParams | generateDefaultLayoutParams()
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
|
public RecyclerView.LayoutParams | generateLayoutParams(android.content.Context c, android.util.AttributeSet attrs)
return new LayoutParams(c, attrs);
|
public RecyclerView.LayoutParams | generateLayoutParams(ViewGroup.LayoutParams lp)
if (lp instanceof ViewGroup.MarginLayoutParams) {
return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
|
public int | getColumnCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state)
if (mOrientation == VERTICAL) {
return mSpanCount;
}
return super.getColumnCountForAccessibility(recycler, state);
|
private int | getFirstChildPosition()
final int childCount = getChildCount();
return childCount == 0 ? 0 : getPosition(getChildAt(0));
|
public int | getGapStrategy()Returns the current gap handling strategy for StaggeredGridLayoutManager.
Staggered grid may have gaps in the layout due to changes in the adapter. To avoid gaps,
StaggeredGridLayoutManager provides 2 options. Check {@link #GAP_HANDLING_NONE} and
{@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS} for details.
By default, StaggeredGridLayoutManager uses {@link #GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS}.
return mGapStrategy;
|
private int | getLastChildPosition()
final int childCount = getChildCount();
return childCount == 0 ? 0 : getPosition(getChildAt(childCount - 1));
|
private int | getMaxEnd(int def)
int maxEnd = mSpans[0].getEndLine(def);
for (int i = 1; i < mSpanCount; i++) {
final int spanEnd = mSpans[i].getEndLine(def);
if (spanEnd > maxEnd) {
maxEnd = spanEnd;
}
}
return maxEnd;
|
private int | getMaxStart(int def)
int maxStart = mSpans[0].getStartLine(def);
for (int i = 1; i < mSpanCount; i++) {
final int spanStart = mSpans[i].getStartLine(def);
if (spanStart > maxStart) {
maxStart = spanStart;
}
}
return maxStart;
|
private int | getMinEnd(int def)
int minEnd = mSpans[0].getEndLine(def);
for (int i = 1; i < mSpanCount; i++) {
final int spanEnd = mSpans[i].getEndLine(def);
if (spanEnd < minEnd) {
minEnd = spanEnd;
}
}
return minEnd;
|
private int | getMinStart(int def)
int minStart = mSpans[0].getStartLine(def);
for (int i = 1; i < mSpanCount; i++) {
final int spanStart = mSpans[i].getStartLine(def);
if (spanStart < minStart) {
minStart = spanStart;
}
}
return minStart;
|
private android.support.v7.widget.StaggeredGridLayoutManager$Span | getNextSpan(LayoutState layoutState)Finds the span for the next view.
final boolean preferLastSpan = preferLastSpan(layoutState.mLayoutDirection);
final int startIndex, endIndex, diff;
if (preferLastSpan) {
startIndex = mSpanCount - 1;
endIndex = -1;
diff = -1;
} else {
startIndex = 0;
endIndex = mSpanCount;
diff = 1;
}
if (layoutState.mLayoutDirection == LAYOUT_END) {
Span min = null;
int minLine = Integer.MAX_VALUE;
final int defaultLine = mPrimaryOrientation.getStartAfterPadding();
for (int i = startIndex; i != endIndex; i += diff) {
final Span other = mSpans[i];
int otherLine = other.getEndLine(defaultLine);
if (otherLine < minLine) {
min = other;
minLine = otherLine;
}
}
return min;
} else {
Span max = null;
int maxLine = Integer.MIN_VALUE;
final int defaultLine = mPrimaryOrientation.getEndAfterPadding();
for (int i = startIndex; i != endIndex; i += diff) {
final Span other = mSpans[i];
int otherLine = other.getStartLine(defaultLine);
if (otherLine > maxLine) {
max = other;
maxLine = otherLine;
}
}
return max;
}
|
public int | getOrientation()
return mOrientation;
|
public boolean | getReverseLayout()Returns whether views are laid out in reverse order or not.
Not that this value is not affected by RecyclerView's layout direction.
return mReverseLayout;
|
public int | getRowCountForAccessibility(RecyclerView.Recycler recycler, RecyclerView.State state)
if (mOrientation == HORIZONTAL) {
return mSpanCount;
}
return super.getRowCountForAccessibility(recycler, state);
|
public int | getSpanCount()Returns the number of spans laid out by StaggeredGridLayoutManager.
return mSpanCount;
|
private int | getSpecForDimension(int dim, int defaultSpec)
if (dim < 0) {
return defaultSpec;
} else {
return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);
}
|
private void | handleUpdate(int positionStart, int itemCountOrToPosition, int cmd)Checks whether it should invalidate span assignments in response to an adapter change.
int minPosition = mShouldReverseLayout ? getLastChildPosition() : getFirstChildPosition();
final int affectedRangeEnd;// exclusive
final int affectedRangeStart;// inclusive
if (cmd == AdapterHelper.UpdateOp.MOVE) {
if (positionStart < itemCountOrToPosition) {
affectedRangeEnd = itemCountOrToPosition + 1;
affectedRangeStart = positionStart;
} else {
affectedRangeEnd = positionStart + 1;
affectedRangeStart = itemCountOrToPosition;
}
} else {
affectedRangeStart = positionStart;
affectedRangeEnd = positionStart + itemCountOrToPosition;
}
mLazySpanLookup.invalidateAfter(affectedRangeStart);
switch (cmd) {
case AdapterHelper.UpdateOp.ADD:
mLazySpanLookup.offsetForAddition(positionStart, itemCountOrToPosition);
break;
case AdapterHelper.UpdateOp.REMOVE:
mLazySpanLookup.offsetForRemoval(positionStart, itemCountOrToPosition);
break;
case AdapterHelper.UpdateOp.MOVE:
// TODO optimize
mLazySpanLookup.offsetForRemoval(positionStart, 1);
mLazySpanLookup.offsetForAddition(itemCountOrToPosition, 1);
break;
}
if (affectedRangeEnd <= minPosition) {
return;
}
int maxPosition = mShouldReverseLayout ? getFirstChildPosition() : getLastChildPosition();
if (affectedRangeStart <= maxPosition) {
requestLayout();
}
|
android.view.View | hasGapsToFix()Checks for gaps if we've reached to the top of the list.
Intermediate gaps created by full span items are tracked via mLaidOutInvalidFullSpan field.
int startChildIndex = 0;
int endChildIndex = getChildCount() - 1;
BitSet mSpansToCheck = new BitSet(mSpanCount);
mSpansToCheck.set(0, mSpanCount, true);
final int firstChildIndex, childLimit;
final int preferredSpanDir = mOrientation == VERTICAL && isLayoutRTL() ? 1 : -1;
if (mShouldReverseLayout) {
firstChildIndex = endChildIndex - 1;
childLimit = startChildIndex - 1;
} else {
firstChildIndex = startChildIndex;
childLimit = endChildIndex;
}
final int nextChildDiff = firstChildIndex < childLimit ? 1 : -1;
for (int i = firstChildIndex; i != childLimit; i += nextChildDiff) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (mSpansToCheck.get(lp.mSpan.mIndex)) {
if (checkSpanForGap(lp.mSpan)) {
return child;
}
mSpansToCheck.clear(lp.mSpan.mIndex);
}
if (lp.mFullSpan) {
continue; // quick reject
}
if (i + nextChildDiff != childLimit) {
View nextChild = getChildAt(i + nextChildDiff);
boolean compareSpans = false;
if (mShouldReverseLayout) {
// ensure child's end is below nextChild's end
int myEnd = mPrimaryOrientation.getDecoratedEnd(child);
int nextEnd = mPrimaryOrientation.getDecoratedEnd(nextChild);
if (myEnd < nextEnd) {
return child;//i should have a better position
} else if (myEnd == nextEnd) {
compareSpans = true;
}
} else {
int myStart = mPrimaryOrientation.getDecoratedStart(child);
int nextStart = mPrimaryOrientation.getDecoratedStart(nextChild);
if (myStart > nextStart) {
return child;//i should have a better position
} else if (myStart == nextStart) {
compareSpans = true;
}
}
if (compareSpans) {
// equal, check span indices.
LayoutParams nextLp = (LayoutParams) nextChild.getLayoutParams();
if (lp.mSpan.mIndex - nextLp.mSpan.mIndex < 0 != preferredSpanDir < 0) {
return child;
}
}
}
}
// everything looks good
return null;
|
public void | invalidateSpanAssignments()For consistency, StaggeredGridLayoutManager keeps a mapping between spans and items.
If you need to cancel current assignments, you can call this method which will clear all
assignments and request a new layout.
mLazySpanLookup.clear();
requestLayout();
|
boolean | isLayoutRTL()
return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
|
private void | layoutDecoratedWithMargins(android.view.View child, int left, int top, int right, int bottom)
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (DEBUG) {
Log.d(TAG, "layout decorated pos: " + lp.getViewLayoutPosition() + ", span:"
+ lp.getSpanIndex() + ", fullspan:" + lp.mFullSpan
+ ". l:" + left + ",t:" + top
+ ", r:" + right + ", b:" + bottom);
}
layoutDecorated(child, left + lp.leftMargin, top + lp.topMargin, right - lp.rightMargin
, bottom - lp.bottomMargin);
|
private void | measureChildWithDecorationsAndMargin(android.view.View child, android.support.v7.widget.StaggeredGridLayoutManager$LayoutParams lp)
if (lp.mFullSpan) {
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(child, mFullSizeSpec,
getSpecForDimension(lp.height, mHeightSpec));
} else {
measureChildWithDecorationsAndMargin(child,
getSpecForDimension(lp.width, mWidthSpec), mFullSizeSpec);
}
} else {
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(child, mWidthSpec,
getSpecForDimension(lp.height, mHeightSpec));
} else {
measureChildWithDecorationsAndMargin(child,
getSpecForDimension(lp.width, mWidthSpec), mHeightSpec);
}
}
|
private void | measureChildWithDecorationsAndMargin(android.view.View child, int widthSpec, int heightSpec)
final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + insets.left,
lp.rightMargin + insets.right);
heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + insets.top,
lp.bottomMargin + insets.bottom);
child.measure(widthSpec, heightSpec);
|
public void | offsetChildrenHorizontal(int dx)
super.offsetChildrenHorizontal(dx);
for (int i = 0; i < mSpanCount; i++) {
mSpans[i].onOffset(dx);
}
|
public void | offsetChildrenVertical(int dy)
super.offsetChildrenVertical(dy);
for (int i = 0; i < mSpanCount; i++) {
mSpans[i].onOffset(dy);
}
|
public void | onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler)
for (int i = 0; i < mSpanCount; i++) {
mSpans[i].clear();
}
|
public void | onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)
super.onInitializeAccessibilityEvent(event);
if (getChildCount() > 0) {
final AccessibilityRecordCompat record = AccessibilityEventCompat
.asRecord(event);
final View start = findFirstVisibleItemClosestToStart(false, true);
final View end = findFirstVisibleItemClosestToEnd(false, true);
if (start == null || end == null) {
return;
}
final int startPos = getPosition(start);
final int endPos = getPosition(end);
if (startPos < endPos) {
record.setFromIndex(startPos);
record.setToIndex(endPos);
} else {
record.setFromIndex(endPos);
record.setToIndex(startPos);
}
}
|
public void | onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, RecyclerView.State state, android.view.View host, android.support.v4.view.accessibility.AccessibilityNodeInfoCompat info)
ViewGroup.LayoutParams lp = host.getLayoutParams();
if (!(lp instanceof LayoutParams)) {
super.onInitializeAccessibilityNodeInfoForItem(host, info);
return;
}
LayoutParams sglp = (LayoutParams) lp;
if (mOrientation == HORIZONTAL) {
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1,
-1, -1,
sglp.mFullSpan, false));
} else { // VERTICAL
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
-1, -1,
sglp.getSpanIndex(), sglp.mFullSpan ? mSpanCount : 1,
sglp.mFullSpan, false));
}
|
public void | onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)
handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.ADD);
|
public void | onItemsChanged(RecyclerView recyclerView)
mLazySpanLookup.clear();
requestLayout();
|
public void | onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount)
handleUpdate(from, to, AdapterHelper.UpdateOp.MOVE);
|
public void | onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)
handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.REMOVE);
|
public void | onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)
handleUpdate(positionStart, itemCount, AdapterHelper.UpdateOp.UPDATE);
|
public void | onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
ensureOrientationHelper();
final AnchorInfo anchorInfo = mAnchorInfo;
anchorInfo.reset();
if (mPendingSavedState != null) {
applyPendingSavedState(anchorInfo);
} else {
resolveShouldLayoutReverse();
anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
}
updateAnchorInfoForLayout(state, anchorInfo);
if (mPendingSavedState == null) {
if (anchorInfo.mLayoutFromEnd != mLastLayoutFromEnd ||
isLayoutRTL() != mLastLayoutRTL) {
mLazySpanLookup.clear();
anchorInfo.mInvalidateOffsets = true;
}
}
if (getChildCount() > 0 && (mPendingSavedState == null ||
mPendingSavedState.mSpanOffsetsSize < 1)) {
if (anchorInfo.mInvalidateOffsets) {
for (int i = 0; i < mSpanCount; i++) {
// Scroll to position is set, clear.
mSpans[i].clear();
if (anchorInfo.mOffset != INVALID_OFFSET) {
mSpans[i].setLine(anchorInfo.mOffset);
}
}
} else {
for (int i = 0; i < mSpanCount; i++) {
mSpans[i].cacheReferenceLineAndClear(mShouldReverseLayout, anchorInfo.mOffset);
}
}
}
detachAndScrapAttachedViews(recycler);
mLaidOutInvalidFullSpan = false;
updateMeasureSpecs();
if (anchorInfo.mLayoutFromEnd) {
// Layout start.
updateLayoutStateToFillStart(anchorInfo.mPosition, state);
fill(recycler, mLayoutState, state);
// Layout end.
updateLayoutStateToFillEnd(anchorInfo.mPosition, state);
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
fill(recycler, mLayoutState, state);
} else {
// Layout end.
updateLayoutStateToFillEnd(anchorInfo.mPosition, state);
fill(recycler, mLayoutState, state);
// Layout start.
updateLayoutStateToFillStart(anchorInfo.mPosition, state);
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
fill(recycler, mLayoutState, state);
}
if (getChildCount() > 0) {
if (mShouldReverseLayout) {
fixEndGap(recycler, state, true);
fixStartGap(recycler, state, false);
} else {
fixStartGap(recycler, state, true);
fixEndGap(recycler, state, false);
}
}
if (!state.isPreLayout()) {
if (getChildCount() > 0 && mPendingScrollPosition != NO_POSITION &&
mLaidOutInvalidFullSpan) {
ViewCompat.postOnAnimation(getChildAt(0), checkForGapsRunnable);
}
mPendingScrollPosition = NO_POSITION;
mPendingScrollPositionOffset = INVALID_OFFSET;
}
mLastLayoutFromEnd = anchorInfo.mLayoutFromEnd;
mLastLayoutRTL = isLayoutRTL();
mPendingSavedState = null; // we don't need this anymore
|
public void | onRestoreInstanceState(android.os.Parcelable state)
if (state instanceof SavedState) {
mPendingSavedState = (SavedState) state;
requestLayout();
} else if (DEBUG) {
Log.d(TAG, "invalid saved state class");
}
|
public android.os.Parcelable | onSaveInstanceState()
if (mPendingSavedState != null) {
return new SavedState(mPendingSavedState);
}
SavedState state = new SavedState();
state.mReverseLayout = mReverseLayout;
state.mAnchorLayoutFromEnd = mLastLayoutFromEnd;
state.mLastLayoutRTL = mLastLayoutRTL;
if (mLazySpanLookup != null && mLazySpanLookup.mData != null) {
state.mSpanLookup = mLazySpanLookup.mData;
state.mSpanLookupSize = state.mSpanLookup.length;
state.mFullSpanItems = mLazySpanLookup.mFullSpanItems;
} else {
state.mSpanLookupSize = 0;
}
if (getChildCount() > 0) {
ensureOrientationHelper();
state.mAnchorPosition = mLastLayoutFromEnd ? getLastChildPosition()
: getFirstChildPosition();
state.mVisibleAnchorPosition = findFirstVisibleItemPositionInt();
state.mSpanOffsetsSize = mSpanCount;
state.mSpanOffsets = new int[mSpanCount];
for (int i = 0; i < mSpanCount; i++) {
int line;
if (mLastLayoutFromEnd) {
line = mSpans[i].getEndLine(Span.INVALID_LINE);
if (line != Span.INVALID_LINE) {
line -= mPrimaryOrientation.getEndAfterPadding();
}
} else {
line = mSpans[i].getStartLine(Span.INVALID_LINE);
if (line != Span.INVALID_LINE) {
line -= mPrimaryOrientation.getStartAfterPadding();
}
}
state.mSpanOffsets[i] = line;
}
} else {
state.mAnchorPosition = NO_POSITION;
state.mVisibleAnchorPosition = NO_POSITION;
state.mSpanOffsetsSize = 0;
}
if (DEBUG) {
Log.d(TAG, "saved state:\n" + state);
}
return state;
|
public void | onScrollStateChanged(int state)
if (state == RecyclerView.SCROLL_STATE_IDLE) {
checkForGaps();
}
|
private boolean | preferLastSpan(int layoutDir)
if (mOrientation == HORIZONTAL) {
return (layoutDir == LAYOUT_START) != mShouldReverseLayout;
}
return ((layoutDir == LAYOUT_START) == mShouldReverseLayout) == isLayoutRTL();
|
private void | prependViewToAllSpans(android.view.View view)
// traverse in reverse so that we end up assigning full span items to 0
for (int i = mSpanCount - 1; i >= 0; i--) {
mSpans[i].prependToSpan(view);
}
|
private void | recycle(RecyclerView.Recycler recycler, LayoutState layoutState, android.support.v7.widget.StaggeredGridLayoutManager$Span updatedSpan, int recycleLine)
if (layoutState.mLayoutDirection == LAYOUT_START) {
// calculate recycle line
int maxStart = getMaxStart(updatedSpan.getStartLine());
recycleFromEnd(recycler, Math.max(recycleLine, maxStart) +
(mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding()));
} else {
// calculate recycle line
int minEnd = getMinEnd(updatedSpan.getEndLine());
recycleFromStart(recycler, Math.min(recycleLine, minEnd) -
(mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding()));
}
|
private void | recycleFromEnd(RecyclerView.Recycler recycler, int line)
final int childCount = getChildCount();
int i;
for (i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (mPrimaryOrientation.getDecoratedStart(child) > line) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.mFullSpan) {
for (int j = 0; j < mSpanCount; j++) {
mSpans[j].popEnd();
}
} else {
lp.mSpan.popEnd();
}
removeAndRecycleView(child, recycler);
} else {
return;// done
}
}
|
private void | recycleFromStart(RecyclerView.Recycler recycler, int line)
if (DEBUG) {
Log.d(TAG, "recycling from start for line " + line);
}
while (getChildCount() > 0) {
View child = getChildAt(0);
if (mPrimaryOrientation.getDecoratedEnd(child) < line) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.mFullSpan) {
for (int j = 0; j < mSpanCount; j++) {
mSpans[j].popStart();
}
} else {
lp.mSpan.popStart();
}
removeAndRecycleView(child, recycler);
} else {
return;// done
}
}
|
private void | resolveShouldLayoutReverse()Calculates the views' layout order. (e.g. from end to start or start to end)
RTL layout support is applied automatically. So if layout is RTL and
{@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
// A == B is the same result, but we rather keep it readable
if (mOrientation == VERTICAL || !isLayoutRTL()) {
mShouldReverseLayout = mReverseLayout;
} else {
mShouldReverseLayout = !mReverseLayout;
}
|
int | scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state)
ensureOrientationHelper();
final int referenceChildPosition;
if (dt > 0) { // layout towards end
mLayoutState.mLayoutDirection = LAYOUT_END;
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD
: ITEM_DIRECTION_TAIL;
referenceChildPosition = getLastChildPosition();
} else {
mLayoutState.mLayoutDirection = LAYOUT_START;
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL
: ITEM_DIRECTION_HEAD;
referenceChildPosition = getFirstChildPosition();
}
mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
final int absDt = Math.abs(dt);
mLayoutState.mAvailable = absDt;
mLayoutState.mExtra = isSmoothScrolling() ? mPrimaryOrientation.getTotalSpace() : 0;
int consumed = fill(recycler, mLayoutState, state);
final int totalScroll;
if (absDt < consumed) {
totalScroll = dt;
} else if (dt < 0) {
totalScroll = -consumed;
} else { // dt > 0
totalScroll = consumed;
}
if (DEBUG) {
Log.d(TAG, "asked " + dt + " scrolled" + totalScroll);
}
mPrimaryOrientation.offsetChildren(-totalScroll);
// always reset this if we scroll for a proper save instance state
mLastLayoutFromEnd = mShouldReverseLayout;
return totalScroll;
|
public int | scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state)
return scrollBy(dx, recycler, state);
|
public void | scrollToPosition(int position)
if (mPendingSavedState != null && mPendingSavedState.mAnchorPosition != position) {
mPendingSavedState.invalidateAnchorPositionInfo();
}
mPendingScrollPosition = position;
mPendingScrollPositionOffset = INVALID_OFFSET;
requestLayout();
|
public void | scrollToPositionWithOffset(int position, int offset)Scroll to the specified adapter position with the given offset from layout start.
Note that scroll position change will not be reflected until the next layout call.
If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchorPositionInfo();
}
mPendingScrollPosition = position;
mPendingScrollPositionOffset = offset;
requestLayout();
|
public int | scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state)
return scrollBy(dy, recycler, state);
|
public void | setGapStrategy(int gapStrategy)Sets the gap handling strategy for StaggeredGridLayoutManager. If the gapStrategy parameter
is different than the current strategy, calling this method will trigger a layout request.
assertNotInLayoutOrScroll(null);
if (gapStrategy == mGapStrategy) {
return;
}
if (gapStrategy != GAP_HANDLING_NONE &&
gapStrategy != GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS) {
throw new IllegalArgumentException("invalid gap strategy. Must be GAP_HANDLING_NONE "
+ "or GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS");
}
mGapStrategy = gapStrategy;
requestLayout();
|
public void | setOrientation(int orientation)Sets the orientation of the layout. StaggeredGridLayoutManager will do its best to keep
scroll position if this method is called after views are laid out.
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException("invalid orientation.");
}
assertNotInLayoutOrScroll(null);
if (orientation == mOrientation) {
return;
}
mOrientation = orientation;
if (mPrimaryOrientation != null && mSecondaryOrientation != null) {
// swap
OrientationHelper tmp = mPrimaryOrientation;
mPrimaryOrientation = mSecondaryOrientation;
mSecondaryOrientation = tmp;
}
requestLayout();
|
public void | setReverseLayout(boolean reverseLayout)Sets whether LayoutManager should start laying out items from the end of the UI. The order
items are traversed is not affected by this call.
For vertical layout, if it is set to true , first item will be at the bottom of
the list.
For horizontal layouts, it depends on the layout direction.
When set to true, If {@link RecyclerView} is LTR, than it will layout from RTL, if
{@link RecyclerView}} is RTL, it will layout from LTR.
assertNotInLayoutOrScroll(null);
if (mPendingSavedState != null && mPendingSavedState.mReverseLayout != reverseLayout) {
mPendingSavedState.mReverseLayout = reverseLayout;
}
mReverseLayout = reverseLayout;
requestLayout();
|
public void | setSpanCount(int spanCount)Sets the number of spans for the layout. This will invalidate all of the span assignments
for Views.
Calling this method will automatically result in a new layout request unless the spanCount
parameter is equal to current span count.
assertNotInLayoutOrScroll(null);
if (spanCount != mSpanCount) {
invalidateSpanAssignments();
mSpanCount = spanCount;
mRemainingSpans = new BitSet(mSpanCount);
mSpans = new Span[mSpanCount];
for (int i = 0; i < mSpanCount; i++) {
mSpans[i] = new Span(i);
}
requestLayout();
}
|
public void | smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position)
LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
final int direction = calculateScrollDirectionForPosition(targetPosition);
if (direction == 0) {
return null;
}
if (mOrientation == HORIZONTAL) {
return new PointF(direction, 0);
} else {
return new PointF(0, direction);
}
}
};
scroller.setTargetPosition(position);
startSmoothScroll(scroller);
|
public boolean | supportsPredictiveItemAnimations()
return mPendingSavedState == null;
|
private void | updateAllRemainingSpans(int layoutDir, int targetLine)
for (int i = 0; i < mSpanCount; i++) {
if (mSpans[i].mViews.isEmpty()) {
continue;
}
updateRemainingSpans(mSpans[i], layoutDir, targetLine);
}
|
private boolean | updateAnchorFromChildren(RecyclerView.State state, android.support.v7.widget.StaggeredGridLayoutManager$AnchorInfo anchorInfo)
// We don't recycle views out of adapter order. This way, we can rely on the first or
// last child as the anchor position.
// Layout direction may change but we should select the child depending on the latest
// layout direction. Otherwise, we'll choose the wrong child.
anchorInfo.mPosition = mLastLayoutFromEnd
? findLastReferenceChildPosition(state.getItemCount())
: findFirstReferenceChildPosition(state.getItemCount());
anchorInfo.mOffset = INVALID_OFFSET;
return true;
|
boolean | updateAnchorFromPendingData(RecyclerView.State state, android.support.v7.widget.StaggeredGridLayoutManager$AnchorInfo anchorInfo)
// Validate scroll position if exists.
if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) {
return false;
}
// Validate it.
if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {
mPendingScrollPosition = NO_POSITION;
mPendingScrollPositionOffset = INVALID_OFFSET;
return false;
}
if (mPendingSavedState == null || mPendingSavedState.mAnchorPosition == NO_POSITION
|| mPendingSavedState.mSpanOffsetsSize < 1) {
// If item is visible, make it fully visible.
final View child = findViewByPosition(mPendingScrollPosition);
if (child != null) {
// Use regular anchor position, offset according to pending offset and target
// child
anchorInfo.mPosition = mShouldReverseLayout ? getLastChildPosition()
: getFirstChildPosition();
if (mPendingScrollPositionOffset != INVALID_OFFSET) {
if (anchorInfo.mLayoutFromEnd) {
final int target = mPrimaryOrientation.getEndAfterPadding() -
mPendingScrollPositionOffset;
anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedEnd(child);
} else {
final int target = mPrimaryOrientation.getStartAfterPadding() +
mPendingScrollPositionOffset;
anchorInfo.mOffset = target - mPrimaryOrientation.getDecoratedStart(child);
}
return true;
}
// no offset provided. Decide according to the child location
final int childSize = mPrimaryOrientation.getDecoratedMeasurement(child);
if (childSize > mPrimaryOrientation.getTotalSpace()) {
// Item does not fit. Fix depending on layout direction.
anchorInfo.mOffset = anchorInfo.mLayoutFromEnd
? mPrimaryOrientation.getEndAfterPadding()
: mPrimaryOrientation.getStartAfterPadding();
return true;
}
final int startGap = mPrimaryOrientation.getDecoratedStart(child)
- mPrimaryOrientation.getStartAfterPadding();
if (startGap < 0) {
anchorInfo.mOffset = -startGap;
return true;
}
final int endGap = mPrimaryOrientation.getEndAfterPadding() -
mPrimaryOrientation.getDecoratedEnd(child);
if (endGap < 0) {
anchorInfo.mOffset = endGap;
return true;
}
// child already visible. just layout as usual
anchorInfo.mOffset = INVALID_OFFSET;
} else {
// Child is not visible. Set anchor coordinate depending on in which direction
// child will be visible.
anchorInfo.mPosition = mPendingScrollPosition;
if (mPendingScrollPositionOffset == INVALID_OFFSET) {
final int position = calculateScrollDirectionForPosition(
anchorInfo.mPosition);
anchorInfo.mLayoutFromEnd = position == LAYOUT_END;
anchorInfo.assignCoordinateFromPadding();
} else {
anchorInfo.assignCoordinateFromPadding(mPendingScrollPositionOffset);
}
anchorInfo.mInvalidateOffsets = true;
}
} else {
anchorInfo.mOffset = INVALID_OFFSET;
anchorInfo.mPosition = mPendingScrollPosition;
}
return true;
|
void | updateAnchorInfoForLayout(RecyclerView.State state, android.support.v7.widget.StaggeredGridLayoutManager$AnchorInfo anchorInfo)
if (updateAnchorFromPendingData(state, anchorInfo)) {
return;
}
if (updateAnchorFromChildren(state, anchorInfo)) {
return;
}
if (DEBUG) {
Log.d(TAG, "Deciding anchor info from fresh state");
}
anchorInfo.assignCoordinateFromPadding();
anchorInfo.mPosition = 0;
|
private void | updateLayoutStateToFillEnd(int anchorPosition, RecyclerView.State state)
mLayoutState.mAvailable = 0;
mLayoutState.mCurrentPosition = anchorPosition;
if (isSmoothScrolling()) {
final int targetPos = state.getTargetScrollPosition();
if (mShouldReverseLayout == targetPos > anchorPosition) {
mLayoutState.mExtra = 0;
} else {
mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace();
}
} else {
mLayoutState.mExtra = 0;
}
mLayoutState.mLayoutDirection = LAYOUT_END;
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD
: ITEM_DIRECTION_TAIL;
|
private void | updateLayoutStateToFillStart(int anchorPosition, RecyclerView.State state)
mLayoutState.mAvailable = 0;
mLayoutState.mCurrentPosition = anchorPosition;
if (isSmoothScrolling()) {
final int targetPos = state.getTargetScrollPosition();
if (mShouldReverseLayout == targetPos < anchorPosition) {
mLayoutState.mExtra = 0;
} else {
mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace();
}
} else {
mLayoutState.mExtra = 0;
}
mLayoutState.mLayoutDirection = LAYOUT_START;
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL
: ITEM_DIRECTION_HEAD;
|
void | updateMeasureSpecs()
mSizePerSpan = mSecondaryOrientation.getTotalSpace() / mSpanCount;
mFullSizeSpec = View.MeasureSpec.makeMeasureSpec(
mSecondaryOrientation.getTotalSpace(), View.MeasureSpec.EXACTLY);
if (mOrientation == VERTICAL) {
mWidthSpec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan, View.MeasureSpec.EXACTLY);
mHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
} else {
mHeightSpec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan, View.MeasureSpec.EXACTLY);
mWidthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
|
private void | updateRemainingSpans(android.support.v7.widget.StaggeredGridLayoutManager$Span span, int layoutDir, int targetLine)
final int deletedSize = span.getDeletedSize();
if (layoutDir == LAYOUT_START) {
final int line = span.getStartLine();
if (line + deletedSize < targetLine) {
mRemainingSpans.set(span.mIndex, false);
}
} else {
final int line = span.getEndLine();
if (line - deletedSize > targetLine) {
mRemainingSpans.set(span.mIndex, false);
}
}
|
private int | updateSpecWithExtra(int spec, int startInset, int endInset)
if (startInset == 0 && endInset == 0) {
return spec;
}
final int mode = View.MeasureSpec.getMode(spec);
if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
return View.MeasureSpec.makeMeasureSpec(
View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
}
return spec;
|