滑動會觸發(fā)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());
向下滑動時
- 獲得靠近頭部的子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();
設置 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)
來循環(huán)添加子View阿弃,而在循環(huán)過程中,添加完子View會執(zhí)行下列代碼來回收屏幕外的子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;
}
}
如圖羞延,如果滑動了absDy的距離渣淳,那么fill時的
remainingSpace
就是圖中的mAvailable
,僅僅比子ViewA
的高度大一點點
- while循環(huán)第一次layoutChunk時伴箩,添加了子View
F
入愧,mConsumed
等于子ViewF
的高度,這時mAvailable -= mConsumed
得到的mAvailable還是大于0的嗤谚,mScrollingOffset += mConsumed
棺蛛,接著調用recycleByLayoutState
,此時會回收recycleChildren(recycler, 0, 1);
子ViewA
巩步,將其添加到Cache緩存或RecyclerViewPool中旁赊,具體查看RecyclerView的緩存分析 - 由于計算后的mAvailable還是大于0,再循環(huán)第二次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;
}
}
- while循環(huán)一次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)