Android NestedScrolling解決滑動衝突問題(2) - fling問題與NestedScroll++

滑動的處理

前一篇文章中分析瞭解決滑動衝突問題的 NestedScroll 接口,也給出瞭解決此類問題的通常性方案:java

NestedScrollingChild側

NestedScrollingChild(後面簡稱NC)處理MotionEvent(通常在onTouchEvent中,若是是ViewGroup還要注意onInterceptTouchEvent的處理,攔截滑動相關的MotionEvent事件),分析用戶滑動操做。spa

在滑動開始時,調用startNestedScroll找到聯動這次滑動的NestedScrollingParent(後面簡稱NP)。code

對於每次用戶交互產生的滑動距離,先調用dispatchNestedPreScroll,詢問聯動NP是否預先處理此滑動,若是NP預先處理了,會給出消耗掉的滑動距離。接口

對於NP預處理剩下的滑動距離,NC決定本身是否處理部分或者所有距離(本身的滑動)。事件

若是NC本身滾動以後,還剩下部分滑動距離,則調用dispatchNestedScroll讓NP自行選擇是否處理最後剩下的這些滑動距離。ci

用戶交互中止滑動,調用stopNestedScroll通知NC中止滑動聯動。get

NestedScrollingParent側

onStartNestedScroll中,決定是否與這次NC發起的滑動請求聯動,若是決定聯動,返回true,不然返回false。返回true以後,會收到onNestedScrollAccepted回調,表示NC贊成與其聯動,能夠開始作初始化操做了;返回false以後,後面的NC聯動操做不會通知此NestedScrollingParent(不會收到後續的onNestedPreScrollonNestedScrollonStopNestedScroll等)。it

onNestedPreScroll中,決定是否預處理滑動單步,並給出消耗掉的滑動距離(不處理則爲0)。io

onNestedScroll中,決定是否消耗NC處理剩下的滑動距離。function

onStopNestedScroll作聯動滑動收尾工做。

經過NC與NP的配合,能夠作到不少複雜的滑動操做。只要分析了界面上外層視圖與內層視圖在滑動時的交互邏輯,就能夠利用這兩個接口實現。

fling的處理

相對於滑動操做,還有一個fling操做,也叫猛劃,指用戶拖住UI元素快速滑動以後擡手,這時會有一個fling事件,通常的操做邏輯是UI元素在擡手以後按照初始速度作減速運動。

NestedScroll 接口也提供了API處理fling事件,在NestedScrollingChilddispatchNestedPreFling通知NP預處理fling事件,dispatchNestedFling通知NP後處理fling事件。

boolean dispatchNestedPreFling(float velocityX, float velocityY);
boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
複製代碼

NestedScrollingParent中對應的接口爲onNestedPreFlingonNestedFling

boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY);
boolean onNestedFling(@NonNull View target, float velocityX, float velocityY, boolean consumed);
複製代碼

經過這幾個接口,可讓NC和NP各自對fling事件作出反應,可是不能像滑動事件同樣聯動。即不能先讓NP預處理 部分 fling 速度,而後NC處理剩下的 部分 fling速度,再將最後剩下的交給NP繼續處理。這種狀況下,上層UI元素與下層UI元素缺少交互,很難作到像滑動操做同樣的UI效果(例如fling時先收起上層視圖部份內容,再滑動下層視圖)。

NestedScroll++

爲了解決此問題,在support包 26.0.0-beta2 版本中引入了 NestedScroll 接口的升級版本(後面稱爲 NestedScroll++ ): NestedScrollingParent2NestedScrollingChild2

NestedScroll++ 接口中,引入了touch type 的概念:對於用戶手指觸摸拖拽產生的滑動事件type爲 ViewCompat.TYPE_TOUCH, fling產生的滑動事件 typeViewCompat.TYPE_NON_TOUCH。滑動接口的相應API中加入了此 type 參數,如onNestedScroll接口改成:

void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type);
複製代碼

NestedScroll++ 接口中,對於滑動事件的處理,與 NestedScroll 接口同樣,只是API中加入了 type 參數。

而對於fling事件的處理,再也不依賴於 dispatchNestedPreFlingdispatchNestedFlingonNestedPreFlingonNestedFling等接口,而是選擇使用與滑動事件相同的處理方式,只是 type 不一樣(爲ViewCompat.TYPE_NON_TOUCH)。

相應的交互邏輯改成:

NestedScrollingChild側

在fling開始時,調用startNestedScroll找到聯動這次滑動的NestedScrollingParent(後面簡稱NP)。

每次刷新視圖時,計算當前時間片由fling產生的滑動距離,先調用dispatchNestedPreScroll,詢問聯動NP是否預先處理此滑動距離,若是NP預先處理了,會給出消耗掉的滑動距離。

對於NP預處理剩下的滑動距離,NC決定本身是否處理部分或者所有距離(本身的滑動)。

若是NC本身滾動以後,還剩下部分滑動距離,則調用dispatchNestedScroll讓NP自行選擇是否處理最後剩下的這些滑動距離。

用戶交互中止滑動,調用stopNestedScroll通知NC中止滑動聯動。

NestedScrollingParent側

onStartNestedScroll中,決定是否與這次NC發起的fling聯動請求,若是決定聯動,返回 true ,不然返回 false 。返回true以後,會收到onNestedScrollAccepted回掉,表示NC贊成與其聯動,能夠開始作初始化操做了;返回false以後,後面的NC聯動操做不會通知此NestedScrollingParent(不會收到後續的onNestedPreScrollonNestedScrollonStopNestedScroll等)。

onNestedPreScroll中,決定是否預處理fling產生的滑動距離,並給出消耗掉的滑動距離(不處理則爲0)。

onNestedScroll中,決定是否消耗NC處理剩下的滑動距離。

onStopNestedScroll作聯動滑動收尾工做。

相關文章
相關標籤/搜索