Navigate to top of recycler view with smooth scroll effect
Work on {@link PeasyRecyclerView.VerticalList} and {@link PeasyRecyclerView.HorizontalList} only
343984
public void smoothScrollToFirst() {
        if (getLinearLayoutManager() != null) {
            final RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getRecyclerView().getContext()) {
                @Override
                protected int getVerticalSnapPreference() {
                    return LinearSmoothScroller.SNAP_TO_START;
                }
            };
            smoothScroller.setTargetPosition(0);
            getLinearLayoutManager().startSmoothScroll(smoothScroller);
        }
    }
611434
void select(ViewHolder selected, int actionState) {
        if (selected == mSelected && actionState == mActionState) {
            return;
        }
        mDragScrollStartTimeInMs = Long.MIN_VALUE;
        final int prevActionState = mActionState;
        // prevent duplicate animations
        endRecoverAnimation(selected, true);
        mActionState = actionState;
        if (actionState == ACTION_STATE_DRAG) {
            // we remove after animation is complete. this means we only elevate the last drag
            // child but that should perform good enough as it is very hard to start dragging a
            // new child before the previous one settles.
            mOverdrawChild = selected.itemView;
            addChildDrawingOrderCallback();
        }
        int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))
                - 1;
        boolean preventLayout = false;
        if (mSelected != null) {
            final ViewHolder prevSelected = mSelected;
            if (prevSelected.itemView.getParent() != null) {
                final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0
                        : swipeIfNecessary(prevSelected);
                releaseVelocityTracker();
                // find where we should animate to
                final float targetTranslateX, targetTranslateY;
                int animationType;
                switch (swipeDir) {
                    case LEFT:
                    case RIGHT:
                    case START:
                    case END:
                        targetTranslateY = 0;
                        targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();
                        break;
                    case UP:
                    case DOWN:
                        targetTranslateX = 0;
                        targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();
                        break;
                    default:
                        targetTranslateX = 0;
                        targetTranslateY = 0;
                }
                if (prevActionState == ACTION_STATE_DRAG) {
                    animationType = ANIMATION_TYPE_DRAG;
                } else if (swipeDir > 0) {
                    animationType = ANIMATION_TYPE_SWIPE_SUCCESS;
                } else {
                    animationType = ANIMATION_TYPE_SWIPE_CANCEL;
                }
                getSelectedDxDy(mTmpPosition);
                final float currentTranslateX = mTmpPosition[0];
                final float currentTranslateY = mTmpPosition[1];
                final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType,
                        prevActionState, currentTranslateX, currentTranslateY,
                        targetTranslateX, targetTranslateY) {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        if (this.mOverridden) {
                            return;
                        }
                        if (swipeDir <= 0) {
                            // this is a drag or failed swipe. recover immediately
                            mCallback.clearView(mRecyclerView, prevSelected);
                            // full cleanup will happen on onDrawOver
                        } else {
                            // wait until remove animation is complete.
                            mPendingCleanup.add(prevSelected.itemView);
                            mIsPendingCleanup = true;
                            if (swipeDir > 0) {
                                // Animation might be ended by other animators during a layout.
                                // We defer callback to avoid editing adapter during a layout.
                                postDispatchSwipe(this, swipeDir);
                            }
                        }
                        // removed from the list after it is drawn for the last time
                        if (mOverdrawChild == prevSelected.itemView) {
                            removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
                        }
                    }
                };
                final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,
                        targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);
                rv.setDuration(duration);
                mRecoverAnimations.add(rv);
                rv.start();
                preventLayout = true;
            } else {
                removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);
                mCallback.clearView(mRecyclerView, prevSelected);
            }
            mSelected = null;
        }
        if (selected != null) {
            mSelectedFlags =
                    (mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)
                            >> (mActionState * DIRECTION_FLAG_COUNT);
            mSelectedStartX = selected.itemView.getLeft();
            mSelectedStartY = selected.itemView.getTop();
            mSelected = selected;
            if (actionState == ACTION_STATE_DRAG) {
                mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            }
        }
        final ViewParent rvParent = mRecyclerView.getParent();
        if (rvParent != null) {
            rvParent.requestDisallowInterceptTouchEvent(mSelected != null);
        }
        if (!preventLayout) {
            mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();
        }
        mCallback.onSelectedChanged(mSelected, mActionState);
        mRecyclerView.invalidate();
    }
1140790
public LinearLayoutManager asVerticalListView() {
        this.presentation = PeasyPresentation.VerticalList;
        resetItemDecorations();
        resetItemAnimator();
        final LinearLayoutManager layoutManager = PeasyRecyclerView.VerticalList.newLayoutManager(getContext());
        getRecyclerView().setLayoutManager(layoutManager);
        getRecyclerView().addItemDecoration(new DividerItemDecoration(getContext(), layoutManager.getOrientation()));
        getRecyclerView().setItemAnimator(new DefaultItemAnimator());
        return layoutManager;
    }
48273
def scroll_up(self, n, pre_dl=None, post_dl=None):
        
        self.delay(pre_dl)
        self.m.scroll(vertical=n)
        self.delay(post_dl)
568100
def fling_forward_vertically(self, *args, **selectors):
        
        return self.device(**selectors).fling.vert.forward()
162356
@Override
    protected int scrollInternalBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Trace.beginSection(TRACE_SCROLL);
        }

        runPreLayout(recycler, state);

        int scrolled = 0;
        try {
            if (!mNoScrolling) {
                scrolled = super.scrollInternalBy(dy, recycler, state);
            } else {
                if (getChildCount() == 0 || dy == 0) {
                    return 0;
                }

                mLayoutState.mRecycle = true;
                ensureLayoutStateExpose();
                final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
                final int absDy = Math.abs(dy);
                updateLayoutStateExpose(layoutDirection, absDy, true, state);
                final int freeScroll = mLayoutState.mScrollingOffset;

                final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);
                if (consumed < 0) {
                    return 0;
                }
                scrolled = absDy > consumed ? layoutDirection * consumed : dy;
            }
        } catch (Exception e) {
            Log.w(TAG, Log.getStackTraceString(e), e);
            if (sDebuggable)
                throw e;

        } finally {
            runPostLayout(recycler, state, scrolled);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Trace.endSection();
        }

        return scrolled;
    }
844829
public <T extends AbsListView> void scrollListToLine(final T view, final int line){
		if(view == null)
			Assert.fail("AbsListView is null!");

		final int lineToMoveTo;
		if(view instanceof GridView) {
			lineToMoveTo = line+1;
		}
		else {
			lineToMoveTo = line;
		}

		inst.runOnMainSync(new Runnable(){
			public void run(){
				view.setSelection(lineToMoveTo);
			}
		});
	}
63218
private View getNextView(RecyclerView parent) {
        View firstView = parent.getChildAt(0);

        // draw the first visible child's header at the top of the view
        int firstPosition = parent.getChildPosition(firstView);
        View firstHeader = getHeaderView(parent, firstPosition);

        for (int i = 0; i < parent.getChildCount(); i++) {

            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();

            if (getOrientation(parent) == LinearLayoutManager.VERTICAL) {

                if (child.getTop() - layoutParams.topMargin > firstHeader.getHeight()) {
                    return child;
                }

            } else {

                if (child.getLeft() - layoutParams.leftMargin > firstHeader.getWidth()) {
                    return child;
                }

            }
        }
        return null;
    }
1354315
private void onScrollImpl(final Vector3Axis offset, final LayoutScroller.OnScrollListener listener) {
        if (!isScrolling()) {
            mScroller = new ScrollingProcessor(offset, listener);
            mScroller.scroll();
        }
    }
806013
private void adaptItemColor() {
        if (adapter != null) {
            RecyclerView.Adapter<?> wrappedAdapter = adapter.getWrappedAdapter();

            if (wrappedAdapter instanceof ArrayRecyclerViewAdapter) {
                ((ArrayRecyclerViewAdapter) wrappedAdapter).setItemColor(itemColor);
            }
        }
    }
908762
private void setSpacingForDirection(@NonNull final Rect outRect,
	                                    @NonNull final RecyclerView.LayoutManager layoutManager,
	                                    final int position,
	                                    final int itemCount) {
		/* Resolve display mode automatically */
		final int displayMode = resolveDisplayMode(layoutManager);

		switch (displayMode) {
			case HORIZONTAL:
				outRect.left = 0;
				outRect.right = position == itemCount - 1 ? 0 : mHorizontalSpace;
				outRect.top = 0;
				outRect.bottom = 0;
				break;
			case VERTICAL:
				outRect.left = 0;
				outRect.right = 0;
				outRect.top = 0;
				outRect.bottom = position == itemCount - 1 ? 0 : mVerticalSpace;
				break;
			case GRID:
				final GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
				final int cols = gridLayoutManager.getSpanCount();
				final int rows = itemCount / cols;

				outRect.left = position % cols == 0 ? 0 : mHorizontalSpace / 2;
				outRect.right = (position + 1) % cols == 0 ? 0 : mHorizontalSpace / 2;
				outRect.top = position < cols ? 0 : mVerticalSpace / 2;
				outRect.bottom = position / cols == rows ? 0 : mVerticalSpace / 2;
				break;
		}
	}
246542
private void allowParentInterceptTouchEvent(ScrollResult scrollResult) {
        if (null != viewParent) {
            if (ContainerScrollType.HORIZONTAL == containerScrollType && !scrollResult.canScrollX
                    && !scaleGestureDetector.isInProgress()) {
                viewParent.requestDisallowInterceptTouchEvent(false);
            } else if (ContainerScrollType.VERTICAL == containerScrollType && !scrollResult.canScrollY
                    && !scaleGestureDetector.isInProgress()) {
                viewParent.requestDisallowInterceptTouchEvent(false);
            }
        }
    }
120956
@Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        View itemView = viewHolder.itemView;
        if (viewHolder.getAdapterPosition() == RecyclerView.NO_POSITION) {
            return;
        }
        if (Math.abs(dX) > Math.abs(dY)) {
            boolean isLeft = dX < 0;
            if (bgPaint == null) {
                bgPaint = new Paint();
                if (horizontalMargin == Integer.MAX_VALUE) {
                    withHorizontalMarginDp(recyclerView.getContext(), 16);
                }
            }
            bgPaint.setColor(isLeft ? bgColorLeft : bgColorRight);

            if (bgPaint.getColor() != Color.TRANSPARENT) {
                int left = isLeft ? itemView.getRight() + (int) dX : itemView.getLeft();
                int right = isLeft ? itemView.getRight() : (itemView.getLeft() + (int) dX);
                c.drawRect(left, itemView.getTop(), right, itemView.getBottom(), bgPaint);
            }

            Drawable drawable = isLeft ? leaveBehindDrawableLeft : leaveBehindDrawableRight;
            if (drawable != null) {
                int itemHeight = itemView.getBottom() - itemView.getTop();
                int intrinsicWidth = drawable.getIntrinsicWidth();
                int intrinsicHeight = drawable.getIntrinsicWidth();

                int left;
                int right;
                if (isLeft) {
                    left = itemView.getRight() - horizontalMargin - intrinsicWidth;
                    right = itemView.getRight() - horizontalMargin;
                } else {
                    left = itemView.getLeft() + horizontalMargin;
                    right = itemView.getLeft() + horizontalMargin + intrinsicWidth;
                }
                int top = itemView.getTop() + (itemHeight - intrinsicHeight) / 2;
                int bottom = top + intrinsicHeight;
                drawable.setBounds(left, top, right, bottom);

                drawable.draw(c);
            }
        }
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }
1324301
function (vnode, options) {
    const scroller = getParentScroller(vnode)
    const scrollDirection = scroller.scrollDirection || 'vertical'

    if (scroller && scroller.$el && vnode.$el) {
      // if it's a list, then the listVnode.scrollDirection is undefined. just
      // assum it is the default value 'vertical'.
      const dSuffix = ({
        horizontal: 'Left',
        vertical: 'Top'
      })[scrollDirection]
      let offset = vnode.$el[`offset${dSuffix}`]

      if (options) {
        offset += Number(options.offset) || 0
        offset *= jud.config.env.scale /* adapt offset to different screen scales. */
      }
      else if (process.env.NODE_ENV === 'development') {
        console.warn('[Vue Render] The second parameter of "scrollToElement" is required, '
          + 'otherwise it may not works well on native.')
      }

      step({
        scrollable: scroller.$el,
        startTime: now(),
        frame: null,
        startPosition: scroller.$el[`scroll${dSuffix}`],
        position: offset,
        method: scrollElement,
        dSuffix: dSuffix
      })
    }
  }
541473
def on_recycle_view(self, index, position):
        
        item = self.list_items[index]
        self.item_mapping[position] = item
        item.recycle_view(position)
744524
@Override
    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {
        // reach the end of this layout
        if (isOutOfRange(layoutState.getCurrentPosition())) {
            return;
        }

        boolean isStartLine = false, isEndLine = false;
        boolean isSecondStartLine = false, isSecondEndLine = false;
        final int currentPosition = layoutState.getCurrentPosition();
        GridRangeStyle rangeStyle = mRangeStyle.findRangeStyleByPosition(currentPosition);

        final int itemDirection = layoutState.getItemDirection();
        final boolean layingOutInPrimaryDirection =
            itemDirection == LayoutStateWrapper.ITEM_DIRECTION_TAIL;

        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();

        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;

        if (layoutInVertical) {
            mTotalSize = helper.getContentWidth() - helper.getPaddingRight() - helper.getPaddingLeft() - rangeStyle
                .getFamilyHorizontalMargin() - rangeStyle.getFamilyHorizontalPadding();
            rangeStyle.mSizePerSpan = (int) ((mTotalSize - (
                rangeStyle.mSpanCount - 1) * rangeStyle.mHGap) * 1.0f / rangeStyle.mSpanCount + 0.5f);
        } else {
            mTotalSize = helper.getContentHeight() - helper.getPaddingBottom() - helper.getPaddingTop() - rangeStyle
                .getFamilyVerticalMargin() - rangeStyle.getFamilyVerticalPadding();
            rangeStyle.mSizePerSpan = (int) ((mTotalSize - (
                rangeStyle.mSpanCount - 1) * rangeStyle.mVGap) * 1.0f / rangeStyle.mSpanCount + 0.5f);
        }

        int count = 0;
        int consumedSpanCount = 0;
        int remainingSpan = rangeStyle.mSpanCount;

        rangeStyle.ensureSpanCount();

        if (!layingOutInPrimaryDirection) {
            // fill the remaining spacing this row
            int itemSpanIndex = getSpanIndex(rangeStyle.mSpanSizeLookup, rangeStyle.mSpanCount, recycler, state, layoutState.getCurrentPosition());
            int itemSpanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, layoutState.getCurrentPosition());


            remainingSpan = itemSpanIndex + itemSpanSize;

            // should find the last element of this row
            if (itemSpanIndex != rangeStyle.mSpanCount - 1) {
                int index = layoutState.getCurrentPosition();
                int revRemainingSpan = rangeStyle.mSpanCount - remainingSpan;
                while (count < rangeStyle.mSpanCount && revRemainingSpan > 0) {
                    // go reverse direction to find views fill current row
                    index -= itemDirection;
                    if (rangeStyle.isOutOfRange(index)) {
                        break;
                    }
                    final int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, index);
                    if (spanSize > rangeStyle.mSpanCount) {
                        throw new IllegalArgumentException("Item at position " + index + " requires " +
                            spanSize + " spans but RangeGridLayoutHelper has only " + rangeStyle.mSpanCount
                            + " spans.");
                    }

                    View view = layoutState.retrieve(recycler, index);
                    if (view == null) {
                        break;
                    }

                    if (!isStartLine) {
                        isStartLine = helper.getReverseLayout() ? index == mRangeStyle.getRange().getUpper().intValue()
                            : index == mRangeStyle.getRange().getLower().intValue();
                    }


                    if (!isEndLine) {
                        isEndLine = helper.getReverseLayout() ? index == mRangeStyle.getRange().getLower().intValue()
                            : index == mRangeStyle.getRange().getUpper().intValue();
                    }


                    revRemainingSpan -= spanSize;
                    if (revRemainingSpan < 0) {
                        break;
                    }


                    consumedSpanCount += spanSize;
                    rangeStyle.mSet[count] = view;
                    count++;
                }

                if (count > 0) {
                    // reverse array
                    int s = 0, e = count - 1;
                    while (s < e) {
                        View temp = rangeStyle.mSet[s];
                        rangeStyle.mSet[s] = rangeStyle.mSet[e];
                        rangeStyle.mSet[e] = temp;
                        s++;
                        e--;
                    }
                }
            }
        }

        while (count < rangeStyle.mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
            int pos = layoutState.getCurrentPosition();
            if (rangeStyle.isOutOfRange(pos)) {
                if (DEBUG) {
                    Log.d(TAG, "pos [" + pos + "] is out of range");
                }
                break;
            }

            final int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, pos);
            if (spanSize > rangeStyle.mSpanCount) {
                throw new IllegalArgumentException("Item at position " + pos + " requires " +
                    spanSize + " spans but GridLayoutManager has only " + rangeStyle.mSpanCount
                    + " spans.");
            }
            remainingSpan -= spanSize;
            if (remainingSpan < 0) {
                break; // item did not fit into this row or column
            }

            View view = layoutState.next(recycler);
            if (view == null) {
                break;
            }

            if (!isStartLine) {
                isStartLine = helper.getReverseLayout() ? pos == mRangeStyle.getRange().getUpper().intValue()
                    : pos == mRangeStyle.getRange().getLower().intValue();
            }
            if (!isSecondStartLine) {
                if (!rangeStyle.equals(mRangeStyle)) {
                    if (mLayoutWithAnchor) {
                        pos = layoutState.getCurrentPosition();
                    }
                    isSecondStartLine = helper.getReverseLayout() ? pos == rangeStyle.getRange().getUpper()
                        .intValue() : pos == rangeStyle.getRange().getLower().intValue();
                }
            }

            if (!isEndLine) {
                isEndLine = helper.getReverseLayout() ? pos == mRangeStyle.getRange().getLower().intValue()
                    : pos == mRangeStyle.getRange().getUpper().intValue();
            }

            if (!isSecondEndLine) {
                if (!rangeStyle.equals(mRangeStyle)) {
                    if (mLayoutWithAnchor) {
                        pos = layoutState.getCurrentPosition();
                    }
                    isSecondEndLine = helper.getReverseLayout() ? pos == rangeStyle.getRange().getLower()
                        .intValue() : pos == rangeStyle.getRange().getUpper().intValue();
                }
            }

            consumedSpanCount += spanSize;
            rangeStyle.mSet[count] = view;
            count++;
        }


        if (count == 0) {
            return;
        }

        int maxSize = 0;


        // we should assign spans before item decor offsets are calculated
        assignSpans(rangeStyle, recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection, helper);

        if (remainingSpan > 0 && (count == consumedSpanCount) && rangeStyle.mIsAutoExpand) {
            //autoExpand only support when each cell occupy one span.
            if (layoutInVertical) {
                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mHGap) / count;
            } else {
                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mVGap) / count;
            }
        } else if (!layingOutInPrimaryDirection && remainingSpan == 0 && (count == consumedSpanCount) && rangeStyle.mIsAutoExpand) {
            //autoExpand only support when each cell occupy one span.
            if (layoutInVertical) {
                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mHGap) / count;
            } else {
                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mVGap) / count;
            }
        }


        boolean weighted = false;
        if (rangeStyle.mWeights != null && rangeStyle.mWeights.length > 0) {
            weighted = true;
            int totalSpace;
            if (layoutInVertical) {
                totalSpace = mTotalSize - (count - 1) * rangeStyle.mHGap;
            } else {
                totalSpace = mTotalSize - (count - 1) * rangeStyle.mVGap;
            }

            // calculate width with weight in percentage

            int eqCnt = 0, remainingSpace = totalSpace;
            int colCnt = (remainingSpan > 0 && rangeStyle.mIsAutoExpand) ? count : rangeStyle.mSpanCount;
            for (int i = 0; i < colCnt; i++) {
                if (i < rangeStyle.mWeights.length && !Float.isNaN(rangeStyle.mWeights[i]) && rangeStyle.mWeights[i] >= 0) {
                    float weight = rangeStyle.mWeights[i];
                    rangeStyle.mSpanCols[i] = (int) (weight * 1.0f / 100 * totalSpace + 0.5f);
                    remainingSpace -= rangeStyle.mSpanCols[i];
                } else {
                    eqCnt++;
                    rangeStyle.mSpanCols[i] = -1;
                }
            }

            if (eqCnt > 0) {
                int eqLength = remainingSpace / eqCnt;
                for (int i = 0; i < colCnt; i++) {
                    if (rangeStyle.mSpanCols[i] < 0) {
                        rangeStyle.mSpanCols[i] = eqLength;
                    }
                }
            }
        }


        for (int i = 0; i < count; i++) {
            View view = rangeStyle.mSet[i];
            helper.addChildView(layoutState, view, layingOutInPrimaryDirection ? -1 : 0);

            int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)), spec;
            if (weighted) {
                final int index = rangeStyle.mSpanIndices[i];
                int spanLength = 0;
                for (int j = 0; j < spanSize; j++) {
                    spanLength += rangeStyle.mSpanCols[j + index];
                }

                spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);
            } else {
                spec = View.MeasureSpec.makeMeasureSpec(rangeStyle.mSizePerSpan * spanSize +
                        Math.max(0, spanSize - 1) * (layoutInVertical ? rangeStyle.mHGap : rangeStyle.mVGap),
                    View.MeasureSpec.EXACTLY);
            }
            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
            if (helper.getOrientation() == VERTICAL) {
                helper.measureChildWithMargins(view, spec, getMainDirSpec(rangeStyle, lp.height, mTotalSize,
                    View.MeasureSpec.getSize(spec), lp.mAspectRatio));
            } else {
                helper.measureChildWithMargins(view,
                    getMainDirSpec(rangeStyle, lp.width, mTotalSize, View.MeasureSpec.getSize(spec),
                        lp.mAspectRatio), View.MeasureSpec.getSize(spec));
            }
            final int size = orientationHelper.getDecoratedMeasurement(view);
            if (size > maxSize) {
                maxSize = size;
            }
        }

        // views that did not measure the maxSize has to be re-measured
        final int maxMeasureSpec = getMainDirSpec(rangeStyle, maxSize, mTotalSize, 0, Float.NaN);
        for (int i = 0; i < count; i++) {
            final View view = rangeStyle.mSet[i];
            if (orientationHelper.getDecoratedMeasurement(view) != maxSize) {
                int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)), spec;
                if (weighted) {
                    final int index = rangeStyle.mSpanIndices[i];
                    int spanLength = 0;
                    for (int j = 0; j < spanSize; j++) {
                        spanLength += rangeStyle.mSpanCols[j + index];
                    }

                    spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);
                } else {
                    spec = View.MeasureSpec.makeMeasureSpec(rangeStyle.mSizePerSpan * spanSize +
                            Math.max(0, spanSize - 1) * (layoutInVertical ? rangeStyle.mHGap : rangeStyle.mVGap),
                        View.MeasureSpec.EXACTLY);
                }

                if (helper.getOrientation() == VERTICAL) {
                    helper.measureChildWithMargins(view, spec, maxMeasureSpec);
                } else {
                    helper.measureChildWithMargins(view, maxMeasureSpec, spec);
                }
            }
        }

        int startSpace = 0, endSpace = 0;

        int secondStartSpace = 0, secondEndSpace = 0;
        boolean isLayoutEnd = layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_END;
        final boolean isOverLapMargin = helper.isEnableMarginOverLap();

        if (isStartLine) {
            startSpace = computeStartSpace(helper, layoutInVertical, isLayoutEnd, isOverLapMargin);
        }
        if (isSecondStartLine) {
            secondStartSpace = (layoutInVertical ? rangeStyle.getMarginTop() + rangeStyle.getPaddingTop()
                : rangeStyle.getMarginLeft() + rangeStyle.getPaddingLeft());
        }

        if (isEndLine) {
            endSpace = layoutInVertical ? mRangeStyle.getMarginBottom() + mRangeStyle.getPaddingBottom()
                : mRangeStyle.getMarginRight() + mRangeStyle.getPaddingRight();
        }
        if (isSecondEndLine) {
            secondEndSpace = (layoutInVertical ? rangeStyle.getMarginBottom() + rangeStyle.getPaddingBottom()
                : rangeStyle.getMarginRight() + rangeStyle.getPaddingRight());
        }


        result.mConsumed = maxSize + startSpace + endSpace + secondStartSpace + secondEndSpace;

        final boolean layoutStart = layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START;
        int consumedGap = 0;
        if (!mLayoutWithAnchor) {
            if (!layoutStart) {
                if (!isStartLine) {
                    if (isSecondStartLine) {
                        consumedGap = (layoutInVertical ? rangeStyle.mParent.mVGap : rangeStyle.mParent.mHGap);
                        if (DEBUG) {
                            Log.d(TAG, "⬇ " + currentPosition + " 1 " + consumedGap + " gap");
                        }
                    } else {
                        consumedGap = (layoutInVertical ? rangeStyle.mVGap : rangeStyle.mHGap);
                        if (DEBUG) {
                            Log.d(TAG, "⬇ " + currentPosition + " 2 " + consumedGap + " gap");
                        }
                    }
                }
            } else {
                if (!isEndLine) {
                    if (isSecondEndLine) {
                        consumedGap = (layoutInVertical ? rangeStyle.mParent.mVGap : rangeStyle.mParent.mHGap);
                        if (DEBUG) {
                            Log.d(TAG, "⬆ " + currentPosition + " 3 " + consumedGap + " gap");
                        }
                    } else {
                        consumedGap = (layoutInVertical ? rangeStyle.mVGap : rangeStyle.mHGap);
                        if (DEBUG) {
                            Log.d(TAG, "⬆ " + currentPosition + " 4 " + consumedGap + " gap");
                        }
                    }
                }
            }
        }
        result.mConsumed += consumedGap;

        if (result.mConsumed <= 0) {
            result.mConsumed = 0;
        }

        int lastUnconsumedSpace = 0;
        /** layoutView() may be triggered by layoutManager's scrollInternalBy() or onFocusSearchFailed() or onLayoutChildren()
         *
         * In case of scrollInternalBy() or onFocusSearchFailed(), layoutState.isRefreshLayout() == false, and layoutState.mOffset = ChildClosestToExpose + alignOffset,
         * see {@link com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx#updateLayoutStateExpose(int, int, boolean, State)},
         * this means last line's layout padding or margin is not really consumed, so considering it before layout new line.
         *
         * In case of onLayoutChildren(), layoutState.isRefreshLayout() == true, and layoutState.mOffset = anchorInfo.mCoordinate = anchorChild.start + alignOffset,
         * see {@link com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx#updateAnchorInfoForLayoutExpose(State, AnchorInfo)},
         * this means last line's layout padding or margin is consumed.
         **/
        if (!layoutState.isRefreshLayout()) {
            if (layoutStart) {
                int lastLinePosition = currentPosition + 1;
                if (!isOutOfRange(lastLinePosition)) {
                    RangeStyle<GridRangeStyle> neighbourRange = mRangeStyle.findRangeStyleByPosition(lastLinePosition);
                    if (neighbourRange.isFirstPosition(lastLinePosition)) {
                        lastUnconsumedSpace = layoutInVertical ? neighbourRange.getMarginTop() + neighbourRange.getPaddingTop()
                            : neighbourRange.getMarginLeft() + neighbourRange.getPaddingLeft();
                        if (DEBUG) {
                            Log.d(TAG, "⬆ " + currentPosition + " 1 " + lastUnconsumedSpace + " last");
                        }
                    }
                }
            } else {
                int lastLinePosition = currentPosition - 1;
                if (!isOutOfRange(lastLinePosition)) {
                    RangeStyle<GridRangeStyle> neighbourRange = mRangeStyle.findRangeStyleByPosition(lastLinePosition);
                    if (neighbourRange.isLastPosition(lastLinePosition)) {
                        lastUnconsumedSpace = layoutInVertical ? neighbourRange.getMarginBottom() + neighbourRange.getPaddingBottom()
                            : neighbourRange.getMarginRight() + neighbourRange.getPaddingRight();
                        if (DEBUG) {
                            Log.d(TAG, "⬇ " + currentPosition + " 2 " + lastUnconsumedSpace + " last");
                        }
                    }
                }
            }
        }

        if (DEBUG) {
            Log.d(TAG,
                (layoutStart ? "⬆ " : "⬇ ") + currentPosition + " consumed " + result.mConsumed + " startSpace " + startSpace + " endSpace "
                    + endSpace + " secondStartSpace " + secondStartSpace + " secondEndSpace " + secondEndSpace + " lastUnconsumedSpace " + lastUnconsumedSpace);
        }

        int left = 0, right = 0, top = 0, bottom = 0;
        if (layoutInVertical) {
            if (layoutStart) {
                bottom = layoutState.getOffset() - endSpace - secondEndSpace - (consumedGap) - lastUnconsumedSpace;
                top = bottom - maxSize;
            } else {
                top = layoutState.getOffset() + startSpace + secondStartSpace + (consumedGap) + lastUnconsumedSpace;
                bottom = top + maxSize;
            }
        } else {
            if (layoutStart) {
                right = layoutState.getOffset() - endSpace - (consumedGap) - lastUnconsumedSpace;
                left = right - maxSize;
            } else {
                left = layoutState.getOffset() + startSpace + (consumedGap) + lastUnconsumedSpace;
                right = left + maxSize;
            }
        }

        for (int i = 0; i < count; i++) {
            View view = rangeStyle.mSet[i];
            final int index = rangeStyle.mSpanIndices[i];

            LayoutParams params = (LayoutParams) view.getLayoutParams();
            if (layoutInVertical) {
                if (weighted) {
                    left = helper.getPaddingLeft() + rangeStyle.getFamilyMarginLeft() + rangeStyle.getFamilyPaddingLeft();
                    for (int j = 0; j < index; j++) {
                        left += rangeStyle.mSpanCols[j] + rangeStyle.mHGap;
                    }
                } else {
                    left = helper.getPaddingLeft() + rangeStyle.getFamilyMarginLeft() + rangeStyle
                        .getFamilyPaddingLeft() + rangeStyle.mSizePerSpan * index + index * rangeStyle.mHGap;
                }

                right = left + orientationHelper.getDecoratedMeasurementInOther(view);
            } else {

                if (weighted) {
                    top = helper.getPaddingTop() + rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop();
                    for (int j = 0; j < index; j++) {
                        top += rangeStyle.mSpanCols[j] + rangeStyle.mVGap;
                    }
                } else {
                    top = helper.getPaddingTop() + rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop()
                        + rangeStyle.mSizePerSpan * index + index * rangeStyle.mVGap;
                }

                bottom = top + orientationHelper.getDecoratedMeasurementInOther(view);
            }

            if (DEBUG) {
                Log.d(TAG, "layout item in position: " + params.getViewPosition() + " with text with SpanIndex: " + index + " into (" +
                    left + ", " + top + ", " + right + ", " + bottom + " )");
            }

            // We calculate everything with View's bounding box (which includes decor and margins)
            // To calculate correct layout position, we subtract margins.
            rangeStyle.layoutChild(view, left, top, right, bottom, helper, false);

            // Consume the available space if the view is not removed OR changed
            if (params.isItemRemoved() || params.isItemChanged()) {
                result.mIgnoreConsumed = true;
            }

            result.mFocusable |= view.isFocusable();
        }


        mLayoutWithAnchor = false;
        Arrays.fill(rangeStyle.mSet, null);
        Arrays.fill(rangeStyle.mSpanIndices, 0);
        Arrays.fill(rangeStyle.mSpanCols, 0);
    }
812689
private View getViewOnRecyclerItemIndex(ViewGroup recyclerView, int recyclerViewIndex, int itemIndex){
		final long endTime = SystemClock.uptimeMillis() + Timeout.getSmallTimeout();
		View view = recyclerView.getChildAt(itemIndex);

		while(view == null){
			final boolean timedOut = SystemClock.uptimeMillis() > endTime;
			if (timedOut){
				Assert.fail("View is null and can therefore not be clicked!");
			}

			sleeper.sleep();
			recyclerView = (ViewGroup) viewFetcher.getIdenticalView(recyclerView);

			if(recyclerView == null){
				recyclerView = (ViewGroup) viewFetcher.getRecyclerView(false, recyclerViewIndex);
			}

			if(recyclerView != null){
				view = recyclerView.getChildAt(itemIndex);
			}
		}
		return view;
	}