FileDocCategorySizeDatePackage
LinearLayoutManager.javaAPI DocAndroid 5.1 API82030Thu Mar 12 22:22:56 GMT 2015android.support.v7.widget

LinearLayoutManager

public class LinearLayoutManager extends RecyclerView.LayoutManager
A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides similar functionality to {@link android.widget.ListView}.

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
public static final int
HORIZONTAL
public static final int
VERTICAL
public static final int
INVALID_OFFSET
private static final float
MAX_SCROLL_FACTOR
While trying to find next view to focus, LayoutManager will not try to scroll more than this factor times the total space of the list. If layout is vertical, total space is the height minus padding, if layout is horizontal, total space is the width minus padding.
int
mOrientation
Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
private LayoutState
mLayoutState
Helper class that keeps temporary layout state. It does not keep state after layout is complete but we still keep a reference to re-use the same object.
OrientationHelper
mOrientationHelper
Many calculations are made depending on orientation. To keep it clean, this interface helps {@link LinearLayoutManager} make those decisions. Based on {@link #mOrientation}, an implementation is lazily created in {@link #ensureLayoutState} method.
private boolean
mLastStackFromEnd
We need to track this so that we can ignore current position when it changes.
private boolean
mReverseLayout
Defines if layout should be calculated from end to start.
boolean
mShouldReverseLayout
This keeps the final value for how LayoutManager should start laying out views. It is calculated by checking {@link #getReverseLayout()} and View's layout direction. {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.
private boolean
mStackFromEnd
Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and it supports both orientations. see {@link android.widget.AbsListView#setStackFromBottom(boolean)}
private boolean
mSmoothScrollbarEnabled
Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}. see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
int
mPendingScrollPosition
When LayoutManager needs to scroll to a position, it sets this variable and requests a layout which will check this variable and re-layout accordingly.
int
mPendingScrollPositionOffset
Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is called.
private boolean
mRecycleChildrenOnDetach
SavedState
mPendingSavedState
final AnchorInfo
mAnchorInfo
Re-used variable to keep anchor information on re-layout. Anchor position and coordinate defines the reference point for LLM while doing a layout.
Constructors Summary
public LinearLayoutManager(android.content.Context context)
Creates a vertical LinearLayoutManager

param
context Current context, will be used to access resources.


                       
       
        this(context, VERTICAL, false);
    
public LinearLayoutManager(android.content.Context context, int orientation, boolean reverseLayout)

param
context Current context, will be used to access resources.
param
orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
param
reverseLayout When set to true, layouts from end to start.

        mAnchorInfo = new AnchorInfo();
        setOrientation(orientation);
        setReverseLayout(reverseLayout);
    
Methods Summary
public voidassertNotInLayoutOrScroll(java.lang.String message)

        if (mPendingSavedState == null) {
            super.assertNotInLayoutOrScroll(message);
        }
    
public booleancanScrollHorizontally()

return
true if {@link #getOrientation()} is {@link #HORIZONTAL}

        return mOrientation == HORIZONTAL;
    
public booleancanScrollVertically()

return
true if {@link #getOrientation()} is {@link #VERTICAL}

        return mOrientation == VERTICAL;
    
public intcomputeHorizontalScrollExtent(RecyclerView.State state)

        return computeScrollExtent(state);
    
public intcomputeHorizontalScrollOffset(RecyclerView.State state)

        return computeScrollOffset(state);
    
public intcomputeHorizontalScrollRange(RecyclerView.State state)

        return computeScrollRange(state);
    
private intcomputeScrollExtent(RecyclerView.State state)

        if (getChildCount() == 0) {
            return 0;
        }
        ensureLayoutState();
        return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper,
                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
                this,  mSmoothScrollbarEnabled);
    
private intcomputeScrollOffset(RecyclerView.State state)

        if (getChildCount() == 0) {
            return 0;
        }
        ensureLayoutState();
        return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
                findFirstVisibleChildClosestToStart(!mSmoothScrollbarEnabled, true),
                findFirstVisibleChildClosestToEnd(!mSmoothScrollbarEnabled, true),
                this, mSmoothScrollbarEnabled, mShouldReverseLayout);
    
private intcomputeScrollRange(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.PointFcomputeScrollVectorForPosition(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 intcomputeVerticalScrollExtent(RecyclerView.State state)

        return computeScrollExtent(state);
    
public intcomputeVerticalScrollOffset(RecyclerView.State state)

        return computeScrollOffset(state);
    
public intcomputeVerticalScrollRange(RecyclerView.State state)

        return computeScrollRange(state);
    
private intconvertFocusDirectionToLayoutDirection(int focusDirection)
Converts a focusDirection to orientation.

param
focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD} or 0 for not applicable
return
{@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.

        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;
        }

    
voidensureLayoutState()

        if (mLayoutState == null) {
            mLayoutState = new LayoutState();
        }
        if (mOrientationHelper == null) {
            mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
        }
    
intfill(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.

param
recycler Current recycler that is attached to RecyclerView
param
layoutState Configuration on how we should fill out the available space.
param
state Context passed by the RecyclerView to control scroll steps.
param
stopOnFocusable If true, filling stops in the first focusable new child
return
Number of pixels that it added. Useful for scoll functions.

        // 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 intfindFirstCompletelyVisibleItemPosition()
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.

return
The adapter position of the first fully visible item or {@link RecyclerView#NO_POSITION} if there aren't any visible items.
see
#findFirstVisibleItemPosition()
see
#findLastCompletelyVisibleItemPosition()

        final View child = findOneVisibleChild(0, getChildCount(), true, false);
        return child == null ? NO_POSITION : getPosition(child);
    
private android.view.ViewfindFirstReferenceChild(int itemCount)

        return findReferenceChild(0, getChildCount(), itemCount);
    
private android.view.ViewfindFirstVisibleChildClosestToEnd(boolean completelyVisible, boolean acceptPartiallyVisible)
Convenience method to find the visible child closes to end. Caller should check if it has enough children.

param
completelyVisible Whether child should be completely visible or not
return
The first visible child closest to end of the layout from user's perspective.

        if (mShouldReverseLayout) {
            return findOneVisibleChild(0, getChildCount(), completelyVisible,
                    acceptPartiallyVisible);
        } else {
            return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
                    acceptPartiallyVisible);
        }
    
private android.view.ViewfindFirstVisibleChildClosestToStart(boolean completelyVisible, boolean acceptPartiallyVisible)
Convenience method to find the visible child closes to start. Caller should check if it has enough children.

param
completelyVisible Whether child should be completely visible or not
return
The first visible child closest to start of the layout from user's perspective.

        if (mShouldReverseLayout) {
            return findOneVisibleChild(getChildCount() - 1, -1, completelyVisible,
                    acceptPartiallyVisible);
        } else {
            return findOneVisibleChild(0, getChildCount(), completelyVisible,
                    acceptPartiallyVisible);
        }
    
public intfindFirstVisibleItemPosition()
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.

return
The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if there aren't any visible items.
see
#findFirstCompletelyVisibleItemPosition()
see
#findLastVisibleItemPosition()

        final View child = findOneVisibleChild(0, getChildCount(), false, true);
        return child == null ? NO_POSITION : getPosition(child);
    
public intfindLastCompletelyVisibleItemPosition()
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.

return
The adapter position of the last fully visible view or {@link RecyclerView#NO_POSITION} if there aren't any visible items.
see
#findLastVisibleItemPosition()
see
#findFirstCompletelyVisibleItemPosition()

        final View child = findOneVisibleChild(getChildCount() - 1, -1, true, false);
        return child == null ? NO_POSITION : getPosition(child);
    
private android.view.ViewfindLastReferenceChild(int itemCount)

        return findReferenceChild(getChildCount() - 1, -1, itemCount);
    
public intfindLastVisibleItemPosition()
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.

return
The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if there aren't any visible items.
see
#findLastCompletelyVisibleItemPosition()
see
#findFirstVisibleItemPosition()

        final View child = findOneVisibleChild(getChildCount() - 1, -1, false, true);
        return child == null ? NO_POSITION : getPosition(child);
    
android.view.ViewfindOneVisibleChild(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.ViewfindReferenceChild(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.ViewfindReferenceChildClosestToEnd(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
A View that can be used an an anchor View.

        return mShouldReverseLayout ? findFirstReferenceChild(state.getItemCount()) :
                findLastReferenceChild(state.getItemCount());
    
private android.view.ViewfindReferenceChildClosestToStart(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
A View that can be used an an anchor View.

        return mShouldReverseLayout ? findLastReferenceChild(state.getItemCount()) :
                findFirstReferenceChild(state.getItemCount());
    
public android.view.ViewfindViewByPosition(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 intfixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren)

return
The final offset amount for children

        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 intfixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren)

return
The final offset amount for children

        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.LayoutParamsgenerateDefaultLayoutParams()
{@inheritDoc}

        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    
private android.view.ViewgetChildClosestToEnd()
Convenience method to find the child closes to end. Caller should check it has enough children.

return
The child closes to end of the layout from user's perspective.

        return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1);
    
private android.view.ViewgetChildClosestToStart()
Convenience method to find the child closes to start. Caller should check it has enough children.

return
The child closes to start of the layout from user's perspective.

        return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0);
    
protected intgetExtraLayoutSpace(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.

return
The extra space that should be laid out (in pixels).

        if (state.hasTargetScrollPosition()) {
            return mOrientationHelper.getTotalSpace();
        } else {
            return 0;
        }
    
public intgetOrientation()
Returns the current orientaion of the layout.

return
Current orientation.
see
#mOrientation
see
#setOrientation(int)

        return mOrientation;
    
public booleangetRecycleChildrenOnDetach()
Returns whether LayoutManager will recycle its children when it is detached from RecyclerView.

return
true if LayoutManager will recycle its children when it is detached from RecyclerView.

        return mRecycleChildrenOnDetach;
    
public booleangetReverseLayout()
Returns if views are laid out from the opposite direction of the layout.

return
If layout is reversed or not.
see
{@link #setReverseLayout(boolean)}

        return mReverseLayout;
    
public booleangetStackFromEnd()

        return mStackFromEnd;
    
protected booleanisLayoutRTL()

        return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
    
public booleanisSmoothScrollbarEnabled()
Returns the current state of the smooth scrollbar feature. It is enabled by default.

return
True if smooth scrollbar is enabled, false otherwise.
see
#setSmoothScrollbarEnabled(boolean)

        return mSmoothScrollbarEnabled;
    
voidlayoutChunk(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 voidlayoutForPredictiveAnimations(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 voidlogChildren()
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, "==============");
    
voidonAnchorReady(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.

param
state
param
anchorInfo Simple data structure to keep anchor point information for the next layout

    
public voidonDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler)

        super.onDetachedFromWindow(view, recycler);
        if (mRecycleChildrenOnDetach) {
            removeAndRecycleAllViews(recycler);
            recycler.clear();
        }
    
public android.view.ViewonFocusSearchFailed(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 voidonInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)

        super.onInitializeAccessibilityEvent(event);
        if (getChildCount() > 0) {
            final AccessibilityRecordCompat record = AccessibilityEventCompat
                    .asRecord(event);
            record.setFromIndex(findFirstVisibleItemPosition());
            record.setToIndex(findLastVisibleItemPosition());
        }
    
public voidonLayoutChildren(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 voidonRestoreInstanceState(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.ParcelableonSaveInstanceState()

        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 voidrecycleByLayoutState(RecyclerView.Recycler recycler, android.support.v7.widget.LinearLayoutManager$LayoutState layoutState)
Helper method to call appropriate recycle method depending on current layout direction

param
recycler Current recycler that is attached to RecyclerView
param
layoutState Current layout state. Right now, this object does not change but we may consider moving it out of this view so passing around as a parameter for now, rather than accessing {@link #mLayoutState}
see
#recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int)
see
#recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int)
see
android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection

        if (!layoutState.mRecycle) {
            return;
        }
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
        } else {
            recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
        }
    
private voidrecycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex)
Recycles children between given indices.

param
startIndex inclusive
param
endIndex exclusive

        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 voidrecycleViewsFromEnd(RecyclerView.Recycler recycler, int dt)
Recycles views that went out of bounds after scrolling towards the start of the layout.

param
recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
param
dt This can be used to add additional padding to the visible area. This is used to detect children that will go out of bounds after scrolling, without actually moving them.

        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 voidrecycleViewsFromStart(RecyclerView.Recycler recycler, int dt)
Recycles views that went out of bounds after scrolling towards the end of the layout.

param
recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
param
dt This can be used to add additional padding to the visible area. This is used to detect children that will go out of bounds after scrolling, without actually moving them.

        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 voidresolveShouldLayoutReverse()
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;
        }
    
intscrollBy(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 intscrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state)
{@inheritDoc}

        if (mOrientation == VERTICAL) {
            return 0;
        }
        return scrollBy(dx, recycler, state);
    
public voidscrollToPosition(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.

param
position Scroll to this adapter position
see
#scrollToPositionWithOffset(int, int)

        mPendingScrollPosition = position;
        mPendingScrollPositionOffset = INVALID_OFFSET;
        if (mPendingSavedState != null) {
            mPendingSavedState.invalidateAnchor();
        }
        requestLayout();
    
public voidscrollToPositionWithOffset(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)}.

param
position Index (starting at 0) of the reference item.
param
offset The distance (in pixels) between the start edge of the item view and start edge of the RecyclerView.
see
#setReverseLayout(boolean)
see
#scrollToPosition(int)

        mPendingScrollPosition = position;
        mPendingScrollPositionOffset = offset;
        if (mPendingSavedState != null) {
            mPendingSavedState.invalidateAnchor();
        }
        requestLayout();
    
public intscrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state)
{@inheritDoc}

        if (mOrientation == HORIZONTAL) {
            return 0;
        }
        return scrollBy(dy, recycler, state);
    
public voidsetOrientation(int orientation)
Sets the orientation of the layout. {@link android.support.v7.widget.LinearLayoutManager} will do its best to keep scroll position.

param
orientation {@link #HORIZONTAL} or {@link #VERTICAL}

        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException("invalid orientation:" + orientation);
        }
        assertNotInLayoutOrScroll(null);
        if (orientation == mOrientation) {
            return;
        }
        mOrientation = orientation;
        mOrientationHelper = null;
        requestLayout();
    
public voidsetRecycleChildrenOnDetach(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.

param
recycleChildrenOnDetach Whether children should be recycled in detach or not.

        mRecycleChildrenOnDetach = recycleChildrenOnDetach;
    
public voidsetReverseLayout(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 voidsetSmoothScrollbarEnabled(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.

param
enabled Whether or not to enable smooth scrollbar.
see
#setSmoothScrollbarEnabled(boolean)

        mSmoothScrollbarEnabled = enabled;
    
public voidsetStackFromEnd(boolean stackFromEnd)
Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}

        assertNotInLayoutOrScroll(null);
        if (mStackFromEnd == stackFromEnd) {
            return;
        }
        mStackFromEnd = stackFromEnd;
        requestLayout();
    
public voidsmoothScrollToPosition(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 booleansupportsPredictiveItemAnimations()

        return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
    
private booleanupdateAnchorFromChildren(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 booleanupdateAnchorFromPendingData(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 voidupdateAnchorInfoForLayout(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 voidupdateLayoutState(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 voidupdateLayoutStateToFillEnd(android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)

        updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate);
    
private voidupdateLayoutStateToFillEnd(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 voidupdateLayoutStateToFillStart(android.support.v7.widget.LinearLayoutManager$AnchorInfo anchorInfo)

        updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate);
    
private voidupdateLayoutStateToFillStart(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;

    
voidvalidateChildOrder()
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");
                }
            }
        }