RecyclerView滑動分析

滑動會觸發(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销部,即向上滑動時

  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)來循環(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;
    }
}

向上滑2.png

如圖羞延,如果滑動了absDy的距離渣淳,那么fill時的remainingSpace就是圖中的mAvailable,僅僅比子ViewA的高度大一點點

  1. while循環(huán)第一次layoutChunk時伴箩,添加了子ViewF入愧,mConsumed等于子ViewF的高度,這時mAvailable -= mConsumed得到的mAvailable還是大于0的嗤谚,mScrollingOffset += mConsumed棺蛛,接著調用recycleByLayoutState,此時會回收recycleChildren(recycler, 0, 1);子ViewA巩步,將其添加到Cache緩存或RecyclerViewPool中旁赊,具體查看RecyclerView的緩存分析
  2. 由于計算后的mAvailable還是大于0,再循環(huán)第二次layoutChunk椅野,添加了子ViewG终畅,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
  1. while循環(huán)一次layoutChunk時炼蛤,添加了子ViewA妖爷,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)

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末炸裆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鲜屏,更是在濱河造成了極大的恐慌烹看,老刑警劉巖国拇,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惯殊,居然都是意外死亡酱吝,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進店門土思,熙熙樓的掌柜王于貴愁眉苦臉地迎上來务热,“玉大人,你說我怎么就攤上這事己儒∑槠瘢” “怎么了?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵闪湾,是天一觀的道長冲甘。 經常有香客問我,道長途样,這世上最難降的妖魔是什么江醇? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮娘纷,結果婚禮上嫁审,老公的妹妹穿的比我還像新娘。我一直安慰自己赖晶,他們只是感情好律适,可當我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著遏插,像睡著了一般捂贿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胳嘲,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天厂僧,我揣著相機與錄音,去河邊找鬼了牛。 笑死颜屠,一個胖子當著我的面吹牛,可吹牛的內容都是我干的鹰祸。 我是一名探鬼主播甫窟,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛙婴!你這毒婦竟也來了粗井?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浇衬,沒想到半個月后懒构,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡耘擂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年胆剧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片醉冤。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡赞赖,死狀恐怖,靈堂內的尸體忽然破棺而出冤灾,到底是詐尸還是另有隱情前域,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布韵吨,位于F島的核電站匿垄,受9級特大地震影響,放射性物質發(fā)生泄漏归粉。R本人自食惡果不足惜椿疗,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糠悼。 院中可真熱鬧届榄,春花似錦、人聲如沸倔喂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽席噩。三九已至班缰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悼枢,已是汗流浹背埠忘。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留馒索,地道東北人莹妒。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像绰上,于是被迫代替她去往敵國和親旨怠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,595評論 2 350

推薦閱讀更多精彩內容