Methods Summary |
---|
public void | assertNotInLayoutOrScroll(java.lang.String message)
if (mPendingSavedState == null) {
super.assertNotInLayoutOrScroll(message);
}
|
public boolean | canScrollHorizontally()
return mOrientation == HORIZONTAL;
|
public boolean | canScrollVertically()
return mOrientation == VERTICAL;
|
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;
}
ensureLayoutState();
return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper,
findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled);
|
private int | computeScrollOffset(RecyclerView.State state)
if (getChildCount() == 0) {
return 0;
}
ensureLayoutState();
return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled, mShouldReverseLayout);
|
private int | computeScrollRange(RecyclerView.State state)
if (getChildCount() == 0) {
return 0;
}
ensureLayoutState();
return ScrollbarHelper.computeScrollRange(state, mOrientationHelper,
findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
this, mSmoothScrollbarEnabled);
|
public android.graphics.PointF | computeScrollVectorForPosition(int targetPosition)
if (getChildCount() == 0) {
return null;
}
final int firstChildPos = getPosition(getChildAt(0));
final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1;
if (mOrientation == HORIZONTAL) {
return new PointF(direction, 0);
} else {
return new PointF(0, direction);
}
|
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 int | convertFocusDirectionToLayoutDirection(int focusDirection)Converts a focusDirection to orientation.
switch (focusDirection) {
case View.FOCUS_BACKWARD:
return LayoutState.LAYOUT_START;
case View.FOCUS_FORWARD:
return LayoutState.LAYOUT_END;
case View.FOCUS_UP:
return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
: LayoutState.INVALID_LAYOUT;
case View.FOCUS_DOWN:
return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
: LayoutState.INVALID_LAYOUT;
case View.FOCUS_LEFT:
return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
: LayoutState.INVALID_LAYOUT;
case View.FOCUS_RIGHT:
return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
: LayoutState.INVALID_LAYOUT;
default:
if (DEBUG) {
Log.d(TAG, "Unknown focus request:" + focusDirection);
}
return LayoutState.INVALID_LAYOUT;
}
|
void | ensureLayoutState()
if (mLayoutState == null) {
mLayoutState = new LayoutState();
}
if (mOrientationHelper == null) {
mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
}
|
int | fill(RecyclerView.Recycler recycler, android.support.v7.widget.LinearLayoutManager$LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable)The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
independent from the rest of the {@link android.support.v7.widget.LinearLayoutManager}
and with little change, can be made publicly available as a helper class.
// 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;
}
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
while (remainingSpace > 0 && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.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 (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
if (DEBUG) {
validateChildOrder();
}
return start - layoutState.mAvailable;
|
public int | findFirstCompletelyVisibleItemPosition()Returns the adapter position of the first fully visible view. This position does not include
adapter changes that were dispatched after the last layout pass.
Note that bounds check is only performed in the current orientation. That means, if
LayoutManager is horizontal, it will only check the view's left and right edges.
final View child = findOneVisibleChild(0, getChildCount(), true, false);
return child == null ? NO_POSITION : getPosition(child);
|
private android.view.View | findFirstReferenceChild(int itemCount)
return findReferenceChild(0, getChildCount(), itemCount);
|
private android.view.View | findFirstVisibleChildClosestToEnd(boolean completelyVisible, boolean acceptPartiallyVisible)Convenience method to find the visible child closes to end. Caller should check if it has
enough children.
if (mShouldReverseLayout) {
return findOneVisibleChild(0, getChildCount(), completelyVisible,
acceptPartiallyVisible);
} else {
return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
acceptPartiallyVisible);
}
|
private android.view.View | findFirstVisibleChildClosestToStart(boolean completelyVisible, boolean acceptPartiallyVisible)Convenience method to find the visible child closes to start. Caller should check if it has
enough children.
if (mShouldReverseLayout) {
return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
acceptPartiallyVisible);
} else {
return findOneVisibleChild(0, getChildCount(), completelyVisible,
acceptPartiallyVisible);
}
|
public int | findFirstVisibleItemPosition()Returns the adapter position of the first visible view. This position does not include
adapter changes that were dispatched after the last layout pass.
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.
LayoutManager may pre-cache some views that are not necessarily visible. Those views
are ignored in this method.
final View child = findOneVisibleChild(0, getChildCount(), false, true);
return child == null ? NO_POSITION : getPosition(child);
|
public int | findLastCompletelyVisibleItemPosition()Returns the adapter position of the last fully visible view. This position does not include
adapter changes that were dispatched after the last layout pass.
Note that bounds check is only performed in the current orientation. That means, if
LayoutManager is horizontal, it will only check the view's left and right edges.
final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false);
return child == null ? NO_POSITION : getPosition(child);
|
private android.view.View | findLastReferenceChild(int itemCount)
return findReferenceChild(getChildCount() - 1, -1, itemCount);
|
public int | findLastVisibleItemPosition()Returns the adapter position of the last visible view. This position does not include
adapter changes that were dispatched after the last layout pass.
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.
LayoutManager may pre-cache some views that are not necessarily visible. Those views
are ignored in this method.
final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true);
return child == null ? NO_POSITION : getPosition(child);
|
android.view.View | findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, boolean acceptPartiallyVisible)
ensureLayoutState();
final int start = mOrientationHelper.getStartAfterPadding();
final int end = mOrientationHelper.getEndAfterPadding();
final int next = toIndex > fromIndex ? 1 : -1;
View partiallyVisible = null;
for (int i = fromIndex; i != toIndex; i+=next) {
final View child = getChildAt(i);
final int childStart = mOrientationHelper.getDecoratedStart(child);
final int childEnd = mOrientationHelper.getDecoratedEnd(child);
if (childStart < end && childEnd > start) {
if (completelyVisible) {
if (childStart >= start && childEnd <= end) {
return child;
} else if (acceptPartiallyVisible && partiallyVisible == null) {
partiallyVisible = child;
}
} else {
return child;
}
}
}
return partiallyVisible;
|
private android.view.View | findReferenceChild(int start, int end, int itemCount)
ensureLayoutState();
View invalidMatch = null;
View outOfBoundsMatch = null;
final int boundsStart = mOrientationHelper.getStartAfterPadding();
final int boundsEnd = mOrientationHelper.getEndAfterPadding();
final int diff = end > start ? 1 : -1;
for (int i = start; i != end; i += diff) {
final View view = getChildAt(i);
final int position = getPosition(view);
if (position >= 0 && position < itemCount) {
if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
if (invalidMatch == null) {
invalidMatch = view; // removed item, least preferred
}
} else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||
mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
if (outOfBoundsMatch == null) {
outOfBoundsMatch = view; // item is not visible, less preferred
}
} else {
return view;
}
}
}
return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
|
private android.view.View | findReferenceChildClosestToEnd(RecyclerView.State state)Among the children that are suitable to be considered as an anchor child, returns the one
closest to the end of the layout.
Due to ambiguous adapter updates or children being removed, some children's positions may be
invalid. This method is a best effort to find a position within adapter bounds if possible.
It also prioritizes children that are within the visible bounds.
return mShouldReverseLayout ? findFirstReferenceChild(state.getItemCount()) :
findLastReferenceChild(state.getItemCount());
|
private android.view.View | findReferenceChildClosestToStart(RecyclerView.State state)Among the children that are suitable to be considered as an anchor child, returns the one
closest to the start of the layout.
Due to ambiguous adapter updates or children being removed, some children's positions may be
invalid. This method is a best effort to find a position within adapter bounds if possible.
It also prioritizes children that are within the visible bounds.
return mShouldReverseLayout ? findLastReferenceChild(state.getItemCount()) :
findFirstReferenceChild(state.getItemCount());
|
public android.view.View | findViewByPosition(int position){@inheritDoc}
final int childCount = getChildCount();
if (childCount == 0) {
return null;
}
final int firstChild = getPosition(getChildAt(0));
final int viewPosition = position - firstChild;
if (viewPosition >= 0 && viewPosition < childCount) {
return getChildAt(viewPosition);
}
return null;
|
private int | fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren)
int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
int fixOffset = 0;
if (gap > 0) {
fixOffset = -scrollBy(-gap, recycler, state);
} else {
return 0; // nothing to fix
}
// move offset according to scroll amount
endOffset += fixOffset;
if (canOffsetChildren) {
// re-calculate gap, see if we could fix it
gap = mOrientationHelper.getEndAfterPadding() - endOffset;
if (gap > 0) {
mOrientationHelper.offsetChildren(gap);
return gap + fixOffset;
}
}
return fixOffset;
|
private int | fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren)
int gap = startOffset - mOrientationHelper.getStartAfterPadding();
int fixOffset = 0;
if (gap > 0) {
// check if we should fix this gap.
fixOffset = -scrollBy(gap, recycler, state);
} else {
return 0; // nothing to fix
}
startOffset += fixOffset;
if (canOffsetChildren) {
// re-calculate gap, see if we could fix it
gap = startOffset - mOrientationHelper.getStartAfterPadding();
if (gap > 0) {
mOrientationHelper.offsetChildren(-gap);
return fixOffset - gap;
}
}
return fixOffset;
|
public RecyclerView.LayoutParams | generateDefaultLayoutParams(){@inheritDoc}
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
|
private android.view.View | getChildClosestToEnd()Convenience method to find the child closes to end. Caller should check it has enough
children.
return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1);
|
private android.view.View | getChildClosestToStart()Convenience method to find the child closes to start. Caller should check it has enough
children.
return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0);
|
protected int | getExtraLayoutSpace(RecyclerView.State state)Returns the amount of extra space that should be laid out by LayoutManager.
By default, {@link android.support.v7.widget.LinearLayoutManager} lays out 1 extra page of
items while smooth scrolling and 0 otherwise. You can override this method to implement your
custom layout pre-cache logic.
Laying out invisible elements will eventually come with performance cost. On the other
hand, in places like smooth scrolling to an unknown location, this extra content helps
LayoutManager to calculate a much smoother scrolling; which improves user experience.
You can also use this if you are trying to pre-layout your upcoming views.
if (state.hasTargetScrollPosition()) {
return mOrientationHelper.getTotalSpace();
} else {
return 0;
}
|
public int | getOrientation()Returns the current orientaion of the layout.
return mOrientation;
|
public boolean | getRecycleChildrenOnDetach()Returns whether LayoutManager will recycle its children when it is detached from
RecyclerView.
return mRecycleChildrenOnDetach;
|
public boolean | getReverseLayout()Returns if views are laid out from the opposite direction of the layout.
return mReverseLayout;
|
public boolean | getStackFromEnd()
return mStackFromEnd;
|
protected boolean | isLayoutRTL()
return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
|
public boolean | isSmoothScrollbarEnabled()Returns the current state of the smooth scrollbar feature. It is enabled by default.
return mSmoothScrollbarEnabled;
|
void | layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, android.support.v7.widget.LinearLayoutManager$LayoutState layoutState, android.support.v7.widget.LinearLayoutManager$LayoutChunkResult result)
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) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (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 (mOrientation == 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);
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();
|
private void | layoutForPredictiveAnimations(RecyclerView.Recycler recycler, RecyclerView.State state, int startOffset, int endOffset)If necessary, layouts new items for predictive animations
// If there are scrap children that we did not layout, we need to find where they did go
// and layout them accordingly so that animations can work as expected.
// This case may happen if new views are added or an existing view expands and pushes
// another view out of bounds.
if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout()
|| !supportsPredictiveItemAnimations()) {
return;
}
// to make the logic simpler, we calculate the size of children and call fill.
int scrapExtraStart = 0, scrapExtraEnd = 0;
final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
final int scrapSize = scrapList.size();
final int firstChildPos = getPosition(getChildAt(0));
for (int i = 0; i < scrapSize; i++) {
RecyclerView.ViewHolder scrap = scrapList.get(i);
final int position = scrap.getLayoutPosition();
final int direction = position < firstChildPos != mShouldReverseLayout
? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END;
if (direction == LayoutState.LAYOUT_START) {
scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
} else {
scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
}
}
if (DEBUG) {
Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart
+ " towards start and " + scrapExtraEnd + " towards end");
}
mLayoutState.mScrapList = scrapList;
if (scrapExtraStart > 0) {
View anchor = getChildClosestToStart();
updateLayoutStateToFillStart(getPosition(anchor), startOffset);
mLayoutState.mExtra = scrapExtraStart;
mLayoutState.mAvailable = 0;
mLayoutState.mCurrentPosition += mShouldReverseLayout ? 1 : -1;
fill(recycler, mLayoutState, state, false);
}
if (scrapExtraEnd > 0) {
View anchor = getChildClosestToEnd();
updateLayoutStateToFillEnd(getPosition(anchor), endOffset);
mLayoutState.mExtra = scrapExtraEnd;
mLayoutState.mAvailable = 0;
mLayoutState.mCurrentPosition += mShouldReverseLayout ? -1 : 1;
fill(recycler, mLayoutState, state, false);
}
mLayoutState.mScrapList = null;
|
private void | logChildren()Used for debugging.
Logs the internal representation of children to default logger.
Log.d(TAG, "internal representation of views on the screen");
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
Log.d(TAG, "item " + getPosition(child) + ", coord:"
+ mOrientationHelper.getDecoratedStart(child));
}
Log.d(TAG, "==============");
|
void | onAnchorReady(RecyclerView.State state, android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)Method called when Anchor position is decided. Extending class can setup accordingly or
even update anchor info if necessary.
|
public void | onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler)
super.onDetachedFromWindow(view, recycler);
if (mRecycleChildrenOnDetach) {
removeAndRecycleAllViews(recycler);
recycler.clear();
}
|
public android.view.View | onFocusSearchFailed(android.view.View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state)
resolveShouldLayoutReverse();
if (getChildCount() == 0) {
return null;
}
final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
if (layoutDir == LayoutState.INVALID_LAYOUT) {
return null;
}
ensureLayoutState();
final View referenceChild;
if (layoutDir == LayoutState.LAYOUT_START) {
referenceChild = findReferenceChildClosestToStart(state);
} else {
referenceChild = findReferenceChildClosestToEnd(state);
}
if (referenceChild == null) {
if (DEBUG) {
Log.d(TAG,
"Cannot find a child with a valid position to be used for focus search.");
}
return null;
}
ensureLayoutState();
final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace());
updateLayoutState(layoutDir, maxScroll, false, state);
mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;
mLayoutState.mRecycle = false;
fill(recycler, mLayoutState, state, true);
final View nextFocus;
if (layoutDir == LayoutState.LAYOUT_START) {
nextFocus = getChildClosestToStart();
} else {
nextFocus = getChildClosestToEnd();
}
if (nextFocus == referenceChild || !nextFocus.isFocusable()) {
return null;
}
return nextFocus;
|
public void | onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)
super.onInitializeAccessibilityEvent(event);
if (getChildCount() > 0) {
final AccessibilityRecordCompat record = AccessibilityEventCompat
.asRecord(event);
record.setFromIndex(findFirstVisibleItemPosition());
record.setToIndex(findLastVisibleItemPosition());
}
|
public void | onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state){@inheritDoc}
// layout algorithm:
// 1) by checking children and other variables, find an anchor coordinate and an anchor
// item position.
// 2) fill towards start, stacking from bottom
// 3) fill towards end, stacking from top
// 4) scroll to fulfill requirements like stack from bottom.
// create layout state
if (DEBUG) {
Log.d(TAG, "is pre layout:" + state.isPreLayout());
}
if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
}
ensureLayoutState();
mLayoutState.mRecycle = false;
// resolve layout direction
resolveShouldLayoutReverse();
mAnchorInfo.reset();
mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
// calculate anchor position and coordinate
updateAnchorInfoForLayout(state, mAnchorInfo);
if (DEBUG) {
Log.d(TAG, "Anchor info:" + mAnchorInfo);
}
// LLM may decide to layout items for "extra" pixels to account for scrolling target,
// caching or predictive animations.
int extraForStart;
int extraForEnd;
final int extra = getExtraLayoutSpace(state);
// default extra space to the tail of the list.
boolean before = state.hasTargetScrollPosition() &&
state.getTargetScrollPosition() < mAnchorInfo.mPosition;
if (before == mAnchorInfo.mLayoutFromEnd) {
extraForEnd = extra;
extraForStart = 0;
} else {
extraForStart = extra;
extraForEnd = 0;
}
extraForStart += mOrientationHelper.getStartAfterPadding();
extraForEnd += mOrientationHelper.getEndPadding();
if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION &&
mPendingScrollPositionOffset != INVALID_OFFSET) {
// if the child is visible and we are going to move it around, we should layout
// extra items in the opposite direction to make sure new items animate nicely
// instead of just fading in
final View existing = findViewByPosition(mPendingScrollPosition);
if (existing != null) {
final int current;
final int upcomingOffset;
if (mShouldReverseLayout) {
current = mOrientationHelper.getEndAfterPadding() -
mOrientationHelper.getDecoratedEnd(existing);
upcomingOffset = current - mPendingScrollPositionOffset;
} else {
current = mOrientationHelper.getDecoratedStart(existing)
- mOrientationHelper.getStartAfterPadding();
upcomingOffset = mPendingScrollPositionOffset - current;
}
if (upcomingOffset > 0) {
extraForStart += upcomingOffset;
} else {
extraForEnd -= upcomingOffset;
}
}
}
int startOffset;
int endOffset;
onAnchorReady(state, mAnchorInfo);
detachAndScrapAttachedViews(recycler);
mLayoutState.mIsPreLayout = state.isPreLayout();
if (mAnchorInfo.mLayoutFromEnd) {
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
mLayoutState.mExtra = extraForStart;
fill(recycler, mLayoutState, state, false);
startOffset = mLayoutState.mOffset;
if (mLayoutState.mAvailable > 0) {
extraForEnd += mLayoutState.mAvailable;
}
// fill towards end
updateLayoutStateToFillEnd(mAnchorInfo);
mLayoutState.mExtra = extraForEnd;
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
} else {
// fill towards end
updateLayoutStateToFillEnd(mAnchorInfo);
mLayoutState.mExtra = extraForEnd;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
if (mLayoutState.mAvailable > 0) {
extraForStart += mLayoutState.mAvailable;
}
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
mLayoutState.mExtra = extraForStart;
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
fill(recycler, mLayoutState, state, false);
startOffset = mLayoutState.mOffset;
}
// changes may cause gaps on the UI, try to fix them.
// TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have
// changed
if (getChildCount() > 0) {
// because layout from end may be changed by scroll to position
// we re-calculate it.
// find which side we should check for gaps.
if (mShouldReverseLayout ^ mStackFromEnd) {
int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);
startOffset += fixOffset;
endOffset += fixOffset;
fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);
startOffset += fixOffset;
endOffset += fixOffset;
} else {
int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);
startOffset += fixOffset;
endOffset += fixOffset;
fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);
startOffset += fixOffset;
endOffset += fixOffset;
}
}
layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
if (!state.isPreLayout()) {
mPendingScrollPosition = NO_POSITION;
mPendingScrollPositionOffset = INVALID_OFFSET;
mOrientationHelper.onLayoutComplete();
}
mLastStackFromEnd = mStackFromEnd;
mPendingSavedState = null; // we don't need this anymore
if (DEBUG) {
validateChildOrder();
}
|
public void | onRestoreInstanceState(android.os.Parcelable state)
if (state instanceof SavedState) {
mPendingSavedState = (SavedState) state;
requestLayout();
if (DEBUG) {
Log.d(TAG, "loaded saved state");
}
} 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();
if (getChildCount() > 0) {
ensureLayoutState();
boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout;
state.mAnchorLayoutFromEnd = didLayoutFromEnd;
if (didLayoutFromEnd) {
final View refChild = getChildClosestToEnd();
state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() -
mOrientationHelper.getDecoratedEnd(refChild);
state.mAnchorPosition = getPosition(refChild);
} else {
final View refChild = getChildClosestToStart();
state.mAnchorPosition = getPosition(refChild);
state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) -
mOrientationHelper.getStartAfterPadding();
}
} else {
state.invalidateAnchor();
}
return state;
|
private void | recycleByLayoutState(RecyclerView.Recycler recycler, android.support.v7.widget.LinearLayoutManager$LayoutState layoutState)Helper method to call appropriate recycle method depending on current layout direction
if (!layoutState.mRecycle) {
return;
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
} else {
recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
}
|
private void | recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex)Recycles children between given indices.
if (startIndex == endIndex) {
return;
}
if (DEBUG) {
Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
}
if (endIndex > startIndex) {
for (int i = endIndex - 1; i >= startIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
} else {
for (int i = startIndex; i > endIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
}
|
private void | recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt)Recycles views that went out of bounds after scrolling towards the start of the layout.
final int childCount = getChildCount();
if (dt < 0) {
if (DEBUG) {
Log.d(TAG, "Called recycle from end with a negative value. This might happen"
+ " during layout changes but may be sign of a bug");
}
return;
}
final int limit = mOrientationHelper.getEnd() - dt;
if (mShouldReverseLayout) {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here
recycleChildren(recycler, 0, i);
return;
}
}
} else {
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here
recycleChildren(recycler, childCount - 1, i);
return;
}
}
}
|
private void | recycleViewsFromStart(RecyclerView.Recycler recycler, int dt)Recycles views that went out of bounds after scrolling towards the end of the layout.
if (dt < 0) {
if (DEBUG) {
Log.d(TAG, "Called recycle from start with a negative value. This might happen"
+ " during layout changes but may be sign of a bug");
}
return;
}
// ignore padding, ViewGroup may not clip children.
final int limit = dt;
final int childCount = getChildCount();
if (mShouldReverseLayout) {
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here
recycleChildren(recycler, childCount - 1, i);
return;
}
}
} else {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here
recycleChildren(recycler, 0, i);
return;
}
}
}
|
private void | resolveShouldLayoutReverse()Calculates the view 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 dy, RecyclerView.Recycler recycler, RecyclerView.State state)
if (getChildCount() == 0 || dy == 0) {
return 0;
}
mLayoutState.mRecycle = true;
ensureLayoutState();
final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
final int absDy = Math.abs(dy);
updateLayoutState(layoutDirection, absDy, true, state);
final int freeScroll = mLayoutState.mScrollingOffset;
final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
if (DEBUG) {
Log.d(TAG, "Don't have any more elements to scroll");
}
return 0;
}
final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
mOrientationHelper.offsetChildren(-scrolled);
if (DEBUG) {
Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
}
return scrolled;
|
public int | scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state){@inheritDoc}
if (mOrientation == VERTICAL) {
return 0;
}
return scrollBy(dx, recycler, state);
|
public void | scrollToPosition(int position)Scroll the RecyclerView to make the position visible.
RecyclerView will scroll the minimum amount that is necessary to make the
target position visible. If you are looking for a similar behavior to
{@link android.widget.ListView#setSelection(int)} or
{@link android.widget.ListView#setSelectionFromTop(int, int)}, use
{@link #scrollToPositionWithOffset(int, int)}.
Note that scroll position change will not be reflected until the next layout call.
mPendingScrollPosition = position;
mPendingScrollPositionOffset = INVALID_OFFSET;
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchor();
}
requestLayout();
|
public void | scrollToPositionWithOffset(int position, int offset)Scroll to the specified adapter position with the given offset from resolved layout
start. Resolved layout start depends on {@link #getReverseLayout()},
{@link ViewCompat#getLayoutDirection(android.view.View)} and {@link #getStackFromEnd()}.
For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling
scrollToPositionWithOffset(10, 20) will layout such that
item[10] 's bottom is 20 pixels above the RecyclerView's bottom.
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)}.
mPendingScrollPosition = position;
mPendingScrollPositionOffset = offset;
if (mPendingSavedState != null) {
mPendingSavedState.invalidateAnchor();
}
requestLayout();
|
public int | scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state){@inheritDoc}
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
|
public void | setOrientation(int orientation)Sets the orientation of the layout. {@link android.support.v7.widget.LinearLayoutManager}
will do its best to keep scroll position.
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException("invalid orientation:" + orientation);
}
assertNotInLayoutOrScroll(null);
if (orientation == mOrientation) {
return;
}
mOrientation = orientation;
mOrientationHelper = null;
requestLayout();
|
public void | setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach)Set whether LayoutManager will recycle its children when it is detached from
RecyclerView.
If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
this flag to true so that views will be avilable to other RecyclerViews
immediately.
Note that, setting this flag will result in a performance drop if RecyclerView
is restored.
mRecycleChildrenOnDetach = recycleChildrenOnDetach;
|
public void | setReverseLayout(boolean reverseLayout)Used to reverse item traversal and layout order.
This behaves similar to the layout change for RTL views. When set to true, first item is
laid out at the end of the UI, second item is laid out before it etc.
For horizontal layouts, it depends on the layout direction.
When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will
layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
from LTR.
If you are looking for the exact same behavior of
{@link android.widget.AbsListView#setStackFromBottom(boolean)}, use
{@link #setStackFromEnd(boolean)}
assertNotInLayoutOrScroll(null);
if (reverseLayout == mReverseLayout) {
return;
}
mReverseLayout = reverseLayout;
requestLayout();
|
public void | setSmoothScrollbarEnabled(boolean enabled)When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
based on the number of visible pixels in the visible items. This however assumes that all
list items have similar or equal widths or heights (depending on list orientation).
If you use a list in which items have different dimensions, the scrollbar will change
appearance as the user scrolls through the list. To avoid this issue, you need to disable
this property.
When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
solely on the number of items in the adapter and the position of the visible items inside
the adapter. This provides a stable scrollbar as the user navigates through a list of items
with varying widths / heights.
mSmoothScrollbarEnabled = enabled;
|
public void | setStackFromEnd(boolean stackFromEnd)Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}
assertNotInLayoutOrScroll(null);
if (mStackFromEnd == stackFromEnd) {
return;
}
mStackFromEnd = stackFromEnd;
requestLayout();
|
public void | smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position)
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return LinearLayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
|
public boolean | supportsPredictiveItemAnimations()
return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
|
private boolean | updateAnchorFromChildren(RecyclerView.State state, android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)Finds an anchor child from existing Views. Most of the time, this is the view closest to
start or end that has a valid position (e.g. not removed).
If a child has focus, it is given priority.
if (getChildCount() == 0) {
return false;
}
View focused = getFocusedChild();
if (focused != null && anchorInfo.assignFromViewIfValid(focused, state)) {
if (DEBUG) {
Log.d(TAG, "decided anchor child from focused view");
}
return true;
}
if (mLastStackFromEnd != mStackFromEnd) {
return false;
}
View referenceChild = anchorInfo.mLayoutFromEnd ? findReferenceChildClosestToEnd(state)
: findReferenceChildClosestToStart(state);
if (referenceChild != null) {
anchorInfo.assignFromView(referenceChild);
// If all visible views are removed in 1 pass, reference child might be out of bounds.
// If that is the case, offset it back to 0 so that we use these pre-layout children.
if (!state.isPreLayout() && supportsPredictiveItemAnimations()) {
// validate this child is at least partially visible. if not, offset it to start
final boolean notVisible =
mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper
.getEndAfterPadding()
|| mOrientationHelper.getDecoratedEnd(referenceChild)
< mOrientationHelper.getStartAfterPadding();
if (notVisible) {
anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
? mOrientationHelper.getEndAfterPadding()
: mOrientationHelper.getStartAfterPadding();
}
}
return true;
}
return false;
|
private boolean | updateAnchorFromPendingData(RecyclerView.State state, android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)If there is a pending scroll position or saved states, updates the anchor info from that
data and returns true
if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) {
return false;
}
// validate scroll position
if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {
mPendingScrollPosition = NO_POSITION;
mPendingScrollPositionOffset = INVALID_OFFSET;
if (DEBUG) {
Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition);
}
return false;
}
// if child is visible, try to make it a reference child and ensure it is fully visible.
// if child is not visible, align it depending on its virtual position.
anchorInfo.mPosition = mPendingScrollPosition;
if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
// Anchor offset depends on how that child was laid out. Here, we update it
// according to our current view bounds
anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
if (anchorInfo.mLayoutFromEnd) {
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
mPendingSavedState.mAnchorOffset;
} else {
anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +
mPendingSavedState.mAnchorOffset;
}
return true;
}
if (mPendingScrollPositionOffset == INVALID_OFFSET) {
View child = findViewByPosition(mPendingScrollPosition);
if (child != null) {
final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
if (childSize > mOrientationHelper.getTotalSpace()) {
// item does not fit. fix depending on layout direction
anchorInfo.assignCoordinateFromPadding();
return true;
}
final int startGap = mOrientationHelper.getDecoratedStart(child)
- mOrientationHelper.getStartAfterPadding();
if (startGap < 0) {
anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding();
anchorInfo.mLayoutFromEnd = false;
return true;
}
final int endGap = mOrientationHelper.getEndAfterPadding() -
mOrientationHelper.getDecoratedEnd(child);
if (endGap < 0) {
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();
anchorInfo.mLayoutFromEnd = true;
return true;
}
anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper
.getTotalSpaceChange())
: mOrientationHelper.getDecoratedStart(child);
} else { // item is not visible.
if (getChildCount() > 0) {
// get position of any child, does not matter
int pos = getPosition(getChildAt(0));
anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos
== mShouldReverseLayout;
}
anchorInfo.assignCoordinateFromPadding();
}
return true;
}
// override layout from end values for consistency
anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
if (mShouldReverseLayout) {
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
mPendingScrollPositionOffset;
} else {
anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +
mPendingScrollPositionOffset;
}
return true;
|
private void | updateAnchorInfoForLayout(RecyclerView.State state, android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)
if (updateAnchorFromPendingData(state, anchorInfo)) {
if (DEBUG) {
Log.d(TAG, "updated anchor info from pending information");
}
return;
}
if (updateAnchorFromChildren(state, anchorInfo)) {
if (DEBUG) {
Log.d(TAG, "updated anchor info from existing children");
}
return;
}
if (DEBUG) {
Log.d(TAG, "deciding anchor info for fresh state");
}
anchorInfo.assignCoordinateFromPadding();
anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;
|
private void | updateLayoutState(int layoutDirection, int requiredSpace, boolean canUseExistingSpace, RecyclerView.State state)
mLayoutState.mExtra = getExtraLayoutSpace(state);
mLayoutState.mLayoutDirection = layoutDirection;
int fastScrollSpace;
if (layoutDirection == LayoutState.LAYOUT_END) {
mLayoutState.mExtra += mOrientationHelper.getEndPadding();
// get the first child in the direction we are going
final View child = getChildClosestToEnd();
// the direction in which we are traversing children
mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
: LayoutState.ITEM_DIRECTION_TAIL;
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
// calculate how much we can scroll without adding new children (independent of layout)
fastScrollSpace = mOrientationHelper.getDecoratedEnd(child)
- mOrientationHelper.getEndAfterPadding();
} else {
final View child = getChildClosestToStart();
mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding();
mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
: LayoutState.ITEM_DIRECTION_HEAD;
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
fastScrollSpace = -mOrientationHelper.getDecoratedStart(child)
+ mOrientationHelper.getStartAfterPadding();
}
mLayoutState.mAvailable = requiredSpace;
if (canUseExistingSpace) {
mLayoutState.mAvailable -= fastScrollSpace;
}
mLayoutState.mScrollingOffset = fastScrollSpace;
|
private void | updateLayoutStateToFillEnd(android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)
updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate);
|
private void | updateLayoutStateToFillEnd(int itemPosition, int offset)
mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset;
mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
LayoutState.ITEM_DIRECTION_TAIL;
mLayoutState.mCurrentPosition = itemPosition;
mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END;
mLayoutState.mOffset = offset;
mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;
|
private void | updateLayoutStateToFillStart(android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)
updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate);
|
private void | updateLayoutStateToFillStart(int itemPosition, int offset)
mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding();
mLayoutState.mCurrentPosition = itemPosition;
mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :
LayoutState.ITEM_DIRECTION_HEAD;
mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START;
mLayoutState.mOffset = offset;
mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;
|
void | validateChildOrder()Used for debugging.
Validates that child views are laid out in correct order. This is important because rest of
the algorithm relies on this constraint.
In default layout, child 0 should be closest to screen position 0 and last child should be
closest to position WIDTH or HEIGHT.
In reverse layout, last child should be closes to screen position 0 and first child should
be closest to position WIDTH or HEIGHT
Log.d(TAG, "validating child count " + getChildCount());
if (getChildCount() < 1) {
return;
}
int lastPos = getPosition(getChildAt(0));
int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0));
if (mShouldReverseLayout) {
for (int i = 1; i < getChildCount(); i++) {
View child = getChildAt(i);
int pos = getPosition(child);
int screenLoc = mOrientationHelper.getDecoratedStart(child);
if (pos < lastPos) {
logChildren();
throw new RuntimeException("detected invalid position. loc invalid? " +
(screenLoc < lastScreenLoc));
}
if (screenLoc > lastScreenLoc) {
logChildren();
throw new RuntimeException("detected invalid location");
}
}
} else {
for (int i = 1; i < getChildCount(); i++) {
View child = getChildAt(i);
int pos = getPosition(child);
int screenLoc = mOrientationHelper.getDecoratedStart(child);
if (pos < lastPos) {
logChildren();
throw new RuntimeException("detected invalid position. loc invalid? " +
(screenLoc < lastScreenLoc));
}
if (screenLoc < lastScreenLoc) {
logChildren();
throw new RuntimeException("detected invalid location");
}
}
}
|