滑动会触发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,即向上滑动时
- 获得靠近底部的子View
final View child = getChildClosestToEnd();
- 将这个子View的底边赋值给mLayoutState.mOffset
mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
- 计算在不添加子View的情况下还能滑多少距离,在不考虑内外边距的情况下就等于底部View的bottom-RecyclerView的高度
scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
- mOrientationHelper.getEndAfterPadding();
// 上面的计算实际上等于scrollingOffset = (mLayoutManager.getDecoratedBottom(view)
+ params.bottomMargin) -(mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom());
向上滑1.png
向下滑动时
- 获得靠近头部的子View
final View child = getChildClosestToStart();
- 将这个子View的顶边赋值给mLayoutState.mOffset
mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
- 计算在不添加子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
的高度大一点点
- while循环第一次layoutChunk时,添加了子View
F
,mConsumed
等于子ViewF
的高度,这时mAvailable -= mConsumed
得到的mAvailable还是大于0的,mScrollingOffset += mConsumed
,接着调用recycleByLayoutState
,此时会回收recycleChildren(recycler, 0, 1);
子ViewA
,将其添加到Cache缓存或RecyclerViewPool中,具体查看RecyclerView的缓存分析 - 由于计算后的mAvailable还是大于0,再循环第二次layoutChunk,添加了子View
G
,mConsumed
等于子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
- while循环一次layoutChunk时,添加了子View
A
,mConsumed
等于子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)
网友评论