1. 程式人生 > >Android NestedScrolling解決滑動衝突問題(2) - fling問題與NestedScroll++

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

滑動的處理

在前一篇文章 Android NestedScrolling解決滑動衝突問題(1) - 相關介面 中,我們分析瞭解決滑動衝突問題的 NestedScroll 介面,也給出瞭解決此類問題的一般性方案:

NestedScrollingChild側

NestedScrollingChild(後面簡稱NC)處理MotionEvent(一般在onTouchEvent中,如果是ViewGroup還要注意onInterceptTouchEvent的處理,攔截滑動相關的MotionEvent事件),分析使用者滑動操作。

在滑動開始時,呼叫startNestedScroll

找到聯動此次滑動的NestedScrollingParent(後面簡稱NP)。

對於每次使用者互動產生的滑動距離,先呼叫dispatchNestedPreScroll,詢問聯動NP是否預先處理此滑動,如果NP預先處理了,會給出消耗掉的滑動距離。

對於NP預處理剩下的滑動距離,NC決定自己是否處理部分或者全部距離(自己的滑動)。

如果NC自己滾動之後,還剩下部分滑動距離,則呼叫dispatchNestedScroll讓NP自行選擇是否處理最後剩下的這些滑動距離。

使用者互動停止滑動,呼叫stopNestedScroll通知NC停止滑動聯動。

NestedScrollingParent側

onStartNestedScroll中,決定是否與此次NC發起的滑動請求聯動,如果決定聯動,返回true,否則返回false。返回true之後,會收到onNestedScrollAccepted回撥,表示NC同意與其聯動,可以開始做初始化操作了;返回false之後,後面的NC聯動操作不會通知此NestedScrollingParent(不會收到後續的onNestedPreScrollonNestedScrollonStopNestedScroll等)。

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

onNestedScroll

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

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做聯動滑動收尾工作。