美文网首页
RecyclerView滑动分析

RecyclerView滑动分析

作者: CP9 | 来源:发表于2018-01-02 15:22 被阅读677次

    滑动会触发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());
    
    向上滑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();
    
    向下滑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;
        }
    }
    
    向上滑2.png
    如图,如果滑动了absDy的距离,那么fill时的remainingSpace就是图中的mAvailable,仅仅比子ViewA的高度大一点点
    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;
        }
    }
    
    向下滑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滑动分析

          本文链接:https://www.haomeiwen.com/subject/xzfnwxtx.html