RecyclerView滑动分析

滑动会触发onTouchEvent方法
MotionEvent.ACTION_MOVE中:

if (scrollByInternal(
        canScrollHorizontally ? dx : 0,
        canScrollVertically ? dy : 0,
        vtev)) {
    getParent().requestDisallowInterceptTouchEvent(true);
}

RecyclerView#scrollByInternal

当x方向或者y方向滑动的距离超过mTouchSlop时,会调用此方法滑动内部

垂直方向的滑动 —— scrollVerticallyBy->scrollBy

调用updateLayoutState(layoutDirection, absDy, true, state),计算在不添加子View的情况下还能滑多少距离

计算layoutDirection,向上滑动时 = LAYOUT_START,向下滑动时 = LAYOUT_END

final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;

layoutDirection == LayoutState.LAYOUT_END,即向上滑动时

  1. 获得靠近底部的子View
    final View child = getChildClosestToEnd();
  2. 将这个子View的底边赋值给mLayoutState.mOffset
    mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
  3. 计算在不添加子View的情况下还能滑多少距离,在不考虑内外边距的情况下就等于底部View的bottom-RecyclerView的高度
scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
        - mOrientationHelper.getEndAfterPadding();
// 上面的计算实际上等于scrollingOffset = (mLayoutManager.getDecoratedBottom(view) 
+ params.bottomMargin) -(mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom());
RecyclerView滑动分析_第1张图片
向上滑1.png

向下滑动时

  1. 获得靠近头部的子View
    final View child = getChildClosestToStart();
  2. 将这个子View的顶边赋值给mLayoutState.mOffset
    mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
  3. 计算在不添加子View的情况下还能滑多少距离,在不考虑内外边距的情况下就等于顶部子View的top
scrollingOffset = -mOrientationHelper.getDecoratedStart(child)
        + mOrientationHelper.getStartAfterPadding();
// 上面的计算实际上等于scrollingOffset = (mLayoutManager.getDecoratedTop(view) - params.topMargin) 
- mLayoutManager.getPaddingTop();
RecyclerView滑动分析_第2张图片
向下滑1.png

设置 mLayoutState.mAvailable 和 mLayoutState.mScrollingOffset 的值

mLayoutState.mAvailable = requiredSpace;
if (canUseExistingSpace) {
    // mLayoutState.mAvailable实际上等于absDy-scrollingOffset
    mLayoutState.mAvailable -= scrollingOffset;
}
mLayoutState.mScrollingOffset = scrollingOffset;

调用fill方法,根据前面的计算得到的mAvailable,mAvailable>0才会添加子View

final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false);

根据RecyclerView测量和布局分析,会根据remainingSpace(layoutState.mAvailable + layoutState.mExtra)来循环添加子View,而在循环过程中,添加完子View会执行下列代码来回收屏幕外的子View:

if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) 
    layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
    if (layoutState.mAvailable < 0) {
        // 这里实际上mScrollingOffset = mScrollingOffset + mConsumed + mAvailable - mConsumed
        layoutState.mScrollingOffset += layoutState.mAvailable;
    }
    recycleByLayoutState(recycler, layoutState);
}

向上滑动时

recycleByLayoutState内部会调用recycleViewsFromStart方法

// limit 实际上等于mScrollingOffset
for (int i = 0; i < childCount; i++) {
    View child = getChildAt(i);
    if (mOrientationHelper.getDecoratedEnd(child) > limit
            || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
        // stop here
        recycleChildren(recycler, 0, i);
        return;
    }
}

RecyclerView滑动分析_第3张图片
向上滑2.png

如图,如果滑动了absDy的距离,那么fill时的 remainingSpace就是图中的 mAvailable,仅仅比子View A的高度大一点点

  1. while循环第一次layoutChunk时,添加了子ViewFmConsumed等于子ViewF的高度,这时mAvailable -= mConsumed得到的mAvailable还是大于0的,mScrollingOffset += mConsumed,接着调用recycleByLayoutState,此时会回收recycleChildren(recycler, 0, 1);子ViewA,将其添加到Cache缓存或RecyclerViewPool中,具体查看RecyclerView的缓存分析
  2. 由于计算后的mAvailable还是大于0,再循环第二次layoutChunk,添加了子ViewGmConsumed等于子ViewG的高度,这时mAvailable -= mConsumed得到的mAvailable是小于0的,mScrollingOffset = mScrollingOffset + mAvailable(这个mAvailable是-mConsumed之前的)相当于absDy,接着调用recycleByLayoutState,还是回收子ViewA

向下滑动时

recycleByLayoutState内部会调用recycleViewsFromEnd方法

for (int i = childCount - 1; i >= 0; i--) {
    View child = getChildAt(i);
    if (mOrientationHelper.getDecoratedStart(child) < limit
            || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
        recycleChildren(recycler, childCount - 1, i);
        return;
    }
}
RecyclerView滑动分析_第4张图片
向下滑2.png
  1. while循环一次layoutChunk时,添加了子ViewAmConsumed等于子ViewA的高度,这时mAvailable -= mConsumed得到的mAvailable等于0,mScrollingOffset += mConsumed,接着调用recycleByLayoutState,此时会回收recycleChildren(recycler, 5, 4);子ViewF,将其添加到Cache缓存或RecyclerViewPool中,具体查看RecyclerView的缓存分析

RecyclerView滑动的核心方法 —— offsetChildrenVertical

滑动的距离scroll的值等于final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;

mOrientationHelper.offsetChildren(-scrolled);->mLayoutManager.offsetChildrenVertical(amount);->mRecyclerView.offsetChildrenVertical(dy)

遍历ChildHelper中的子View,让所有子View都offsetTopAndBottom(dy)

你可能感兴趣的:(RecyclerView滑动分析)