1. 一文搞懂 BottomSheetBehavior

        共 3932字,需瀏覽 8分鐘

         ·

        2021-02-05 12:48


        1. 引言

        BottomSheetBehavior能實(shí)現(xiàn)怎樣的效果,一圖勝千言。c82955f174147da58d9a187a1f12ac39.webp

        如果僅僅是實(shí)現(xiàn)上下拖動(dòng)和隱藏的功能。拋開(kāi)BottomSheetBehavior自己實(shí)現(xiàn)也不難,在沒(méi)有CoordinatorLayout的年代,這種效果往往是純手工打造。既然如此為何Google要專門設(shè)計(jì)BottomSheetBehavior呢?為了搞清楚這個(gè)問(wèn)題,我查閱源碼探究了一番,確實(shí)發(fā)現(xiàn)了一些隱秘的角落。我將從以下幾個(gè)方面講解BottomSheetBehavior的設(shè)計(jì)思路

        1. 講解BottomSheetBehavior的幾種狀態(tài)
        2. 講解BottomSheetBehavior的事件分發(fā)
        3. 講解BottomSheetBehavior如何處理嵌套滑動(dòng)
        4. 實(shí)現(xiàn)高德地圖首頁(yè)效果,歡迎關(guān)注字節(jié)小站微信公眾號(hào)號(hào)
        2. BottomSheetBehavior的幾種狀態(tài)

        BottomSheetBehavior一共有6種狀態(tài)

        1. STATE_EXPANDED ?全部展開(kāi)狀態(tài)
        2. STATE_COLLAPSED 收起狀態(tài)
        3. STATE_DRAGGING ?拖動(dòng)狀態(tài)
        4. STATE_SETTLING
        5. STATE_HIDDEN ? ?隱藏狀態(tài)
        6. STATE_HALF_EXPANDED 半展開(kāi)狀態(tài)
        7b4ea4b236846fe759a204ecefc0d057.webp

        系統(tǒng)通過(guò)哪種方式實(shí)現(xiàn)每種狀態(tài)不同的偏移量呢?

        1. layout階段通過(guò)ViewCompat.offsetTopAndBottom(child, offset)實(shí)現(xiàn)偏移量
        2. 用戶觸摸交互階段通過(guò)ViewDragHelper.dragTo(left,top,dx,dy)實(shí)現(xiàn)偏移量

        2.1 Layout階段

        5e639bdfa830bb9b2bd2730c482ca4ca.webpBottomSheetBehavior#onLayoutChild

        Layout階段最后會(huì)通過(guò)findScrollingChild方法,尋找開(kāi)啟了嵌套滑動(dòng)的后代View。其實(shí)這就是Google單獨(dú)研發(fā)出BottomSheetBehavior的主要考量。滿足支持嵌套滑動(dòng)的BottomSheet效果。

        2.2 用戶觸摸交互階段

        f7f6566fc18a338d1bbb805201bd6d7e.webpb9ba59901a6a429522204ac15c2af7ec.webp

        2.3 狀態(tài)對(duì)應(yīng)的偏移量

        狀態(tài)偏移量
        STATE_COLLAPSEDcollapsedOffset
        STATE_EXPANDEDgetExpandedOffset()
        STATE_HALF_EXPANDEDhalfExpandedOffset
        STATE_HIDDENparentHeight

        1. 計(jì)算 collapsedOffset

        5e27f1cf90f45b68438782ac17e31566.webp
        變量名默認(rèn)值
        PEEK_HEIGHT_AUTO常量值-1
        peekHeightMin默認(rèn)值64dp,用戶不可修改
        peekHeightAuto默認(rèn)值true,用戶可設(shè)置
        peekHeight默認(rèn)值0,如果設(shè)置為PEEK_HEIGHT_AUTO peekHeightAuto為true否則為false,如果設(shè)置小于-1則為0
        fitToContents默認(rèn)值true,用戶可設(shè)置
        fitToContentOffsetMath.max(0, parentHeight - child.getHeight())

        peekHeight默認(rèn)值為0。設(shè)置邏輯如下

        1358786e6c410a405a72f50ced423ec7.webp
        1. height為-1,則peekHeightAuto設(shè)置為true。
        2. 否則peekHeightAuto為false,而且peekHeight最小值為0。5e27f1cf90f45b68438782ac17e31566.webp

        計(jì)算collapsedOffset值有四種情況

        CasepeekHeightAutofitToContents
        case1truetrue
        case2truefalse
        case3falsetrue
        case4falsefalse

        返回值

        Case返回值
        Case1Math.max(parentHeight - Math.max(peekHeightMin, parentHeight - parentWidth * 9 / 16), fitToContentsOffset)
        Case2parentHeight-Math.max(peekHeightMin, parentHeight - parentWidth * 9 / 16)
        Case3Math.max(parentHeight - peekHeight, fitToContentsOffset)
        Case4parentHeight - peekHeight

        2. 計(jì)算 halfExpandedOffset

        e83f16fc1dc8be42414591ff96879aa4.webp

        3. 計(jì)算 expandedOffset

        3f337742ac191222d18b3c179b1e47e3.webp

        4.如何固定BottomSheetBehavior的高度?

        了解這些值的計(jì)算有什么好處。假設(shè)我想讓BottomSheetBehavior,固定高度,不能向上滑也不能向下滑。那我們則需要將collapsedOffset和expandedOffset設(shè)置為一樣的值才行。7f5be18b37f6df379a47abf697a51ed1.webp

        代碼如下5bdd42cf41898b3483bb08d0b127e082.webp

        為了良好的閱讀體驗(yàn)沒(méi)有使用代碼塊呈現(xiàn)代碼,如果你想獲取代碼請(qǐng)?jiān)L問(wèn)github代碼庫(kù)

        3. 講解BottomSheetBehavior的事件分發(fā)

        學(xué)習(xí)Android事件分發(fā)是有方法的。我總結(jié)為"三板斧"分析法

        1. 源碼分析
        2. 場(chǎng)景化
        3. 樹(shù)形圖分析

        3.1 三板斧之源碼分析

        e85213b657c1013bc2504d0de3906aec.webp

        從onInterceptTouchEvent的代碼中,可以看到viewDragHelper.shouldInterceptTouchEvent(event),說(shuō)明攔截方法會(huì)讓ViewDragHelper方法處理。

        ViewDragHelper的初始化,會(huì)傳入ViewDragHelper.Callback dragCallback對(duì)象,該對(duì)象的boolean tryCaptureView(View child, int pointerId)方法決定viewDragHelper.shouldInterceptTouchEvent的返回值。

        bfa191607929fad83eea616000c2e1cf.webp1d35e7acab90d784fb50fb54504b5480.webp

        onInterceptTouchEvent的攔截邏輯如下

        9b71d092171ee5a540b4cfb97202ac0f.webp

        onTouchEvent主要交由ViewDragHelper#processTouchEvent處理,如果是Move事件最終會(huì)調(diào)用dragTo方法進(jìn)行移動(dòng)

        b4e09f77c476a24e9babc0279f9cc306.webp

        3.2 三板斧之場(chǎng)景化和樹(shù)形圖分析

        假設(shè)有場(chǎng)景如下,用戶可以在HeadLayout、NestedScrollingChild,TopMostLayout區(qū)域內(nèi)上下滑動(dòng)。這三個(gè)case,事件的處理路徑如何呢?c0bbc7f26d74c36ccc8f2f1d30c82e3c.webp

        轉(zhuǎn)化成樹(shù)形圖如下

        設(shè)置BottomSheetBehavior為L(zhǎng)inearLayout的Behavior4817afbecfc98e76d8995c0b4a1f21ee.webp

        3.2.1. 在HeadLayout區(qū)域內(nèi)上下滑動(dòng)

        1. Down事件處理,初始狀態(tài),在ViewDragHelper的shouldInterceptTouchEvent方法中不會(huì)調(diào)用tryCaptureViewForDrag方法,該方法返回false。在BottomSheetBehavior onInterceptTouchEvent中完整事件路徑如下,紅線表示事件的分發(fā)路徑5b08d7823d7a2d2535ec4f3a8b9caefa.webp

        結(jié)合樹(shù)形圖分析。由于BottomSheetBehavior不攔截事件。Down事件分發(fā)流程如下

        d00112cef6fcc1da9989656477eb5c5a.webp

        最終會(huì)調(diào)用到BottomSheetBehavior的onTouchEvent方法,會(huì)調(diào)用到ViewDragHelper的processTouchEvent方法

        65aec3c6c6885fdb67f85a9a6eaffb43.webp

        最終會(huì)將ViewDragHelper的dragState設(shè)置為STATE_DRAGGING

        1. MOVE事件在BottomSheetBehavior onInterceptTouchEvent分發(fā)流程如下
        3c139bc093cce95b280ee0ca5c0a885d.webp

        接下來(lái)直接調(diào)用 BottomSheetBehavior 的onTouchEvent方法。同樣調(diào)用到ViewDragHelper的processTouchEvent方法90e74c6e8345cb2066a212f9b390db90.webp

        3.2.2. 在NestedScrollingView區(qū)域內(nèi)上下滑動(dòng)

        1.Down事件分發(fā)到BottomSheetBehavior的onInterceptTouchEvent分發(fā)流程如下cebc16694368975b982e01c1e5dc72f0.webp

        由于不攔截。Down事件分發(fā)給NestedScrollingChild,NestedScrollingChild會(huì)啟動(dòng)嵌套滑動(dòng),與BottomSheetBehavior配合完成嵌套滑動(dòng)

        2.Move事件分發(fā)流程比較復(fù)雜,當(dāng)在NSC中Move的距離沒(méi)達(dá)到閾值時(shí),MOVE會(huì)繼續(xù)分發(fā)到BottomSheetBehavior的onInterceptTouchEvent中,當(dāng)在NSC中MOVE距離達(dá)到閾值時(shí),會(huì)調(diào)用parent.requestDisallowInterceptTouchEvent(true)從此直達(dá)NSC,就是純粹的嵌套滑動(dòng)了。

        8614bba195a8a1ca80da3fdec654dfb6.webp

        接下來(lái)事件交由NSC分發(fā),當(dāng)MOVE距離大于閾值時(shí),事件直接交由NSC處理。

        efa2f43984e3c441334763bd3d0d8170.webp

        3.2.2. 在TopMostLayout區(qū)域內(nèi)上下滑動(dòng),該區(qū)域與NSC區(qū)域沒(méi)有交集

        Down事件同上5b08d7823d7a2d2535ec4f3a8b9caefa.webp

        MOVE事件,當(dāng)距離>ViewDragHelper閾值時(shí)

        abb859fccb7fc81ff2c5ac14e26549e2.webp

        由于MOVE事件攔截了,會(huì)交由BottomSheetBehavior onTouchEvent處理,如下圖895dccba487628c3521ca17353f10158.webp

        4. 結(jié)束

        至此已基本講解完BottomSheetBehavior的事件分發(fā)機(jī)制。具體細(xì)節(jié)未能盡善盡美。紙上得來(lái)終覺(jué)淺,希望讀者可以結(jié)合文章去探索源碼。下次我將用BottomSheetBehavior來(lái)實(shí)現(xiàn)高德地圖首頁(yè)效果。歡迎持續(xù)關(guān)注

        7605e70be440c079c12d0d122af9b9c9.webp

        —————END—————


        我是南塵,只做比心的公眾號(hào),歡迎關(guān)注我。

        推薦閱讀:

        nanchen,是一個(gè)怎樣的公眾號(hào)?

        Android 代碼規(guī)范大全


        歡迎關(guān)注南塵的公眾號(hào):nanchen
        做不完的開(kāi)源,寫不完的矯情,只做比心的公眾號(hào),如果你喜歡,你可以選擇分享給大家。如果你有好的文章,歡迎投稿,讓我們一起來(lái)分享。
        ? ? ??? ??長(zhǎng)按上方二維碼關(guān)注? ? ? ? 做不完的開(kāi)源,寫不完的矯情? ? ? ? 一起來(lái)看 nanchen 同學(xué)的成長(zhǎng)筆記


        瀏覽 81
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 日日cao| 日本不卡网 | 成人视频网站在线播放 | 自拍偷偷操 | 欧美成人18禁 |