前一篇文章中分析瞭解決滑動衝突問題的 NestedScroll 接口,也給出瞭解決此類問題的通常性方案:java
NestedScrollingChild
(後面簡稱NC)處理MotionEvent
(通常在onTouchEvent
中,若是是ViewGroup
還要注意onInterceptTouchEvent
的處理,攔截滑動相關的MotionEvent
事件),分析用戶滑動操做。spa
在滑動開始時,調用startNestedScroll
找到聯動這次滑動的NestedScrollingParent
(後面簡稱NP)。code
對於每次用戶交互產生的滑動距離,先調用dispatchNestedPreScroll
,詢問聯動NP是否預先處理此滑動,若是NP預先處理了,會給出消耗掉的滑動距離。接口
對於NP預處理剩下的滑動距離,NC決定本身是否處理部分或者所有距離(本身的滑動)。事件
若是NC本身滾動以後,還剩下部分滑動距離,則調用dispatchNestedScroll
讓NP自行選擇是否處理最後剩下的這些滑動距離。ci
用戶交互中止滑動,調用stopNestedScroll
通知NC中止滑動聯動。get
在onStartNestedScroll
中,決定是否與這次NC發起的滑動請求聯動,若是決定聯動,返回true
,不然返回false
。返回true
以後,會收到onNestedScrollAccepted
回調,表示NC贊成與其聯動,能夠開始作初始化操做了;返回false以後,後面的NC聯動操做不會通知此NestedScrollingParent
(不會收到後續的onNestedPreScroll
、onNestedScroll
、onStopNestedScroll
等)。it
在onNestedPreScroll
中,決定是否預處理滑動單步,並給出消耗掉的滑動距離(不處理則爲0)。io
在onNestedScroll
中,決定是否消耗NC處理剩下的滑動距離。function
在onStopNestedScroll
作聯動滑動收尾工做。
經過NC與NP的配合,能夠作到不少複雜的滑動操做。只要分析了界面上外層視圖與內層視圖在滑動時的交互邏輯,就能夠利用這兩個接口實現。
相對於滑動操做,還有一個fling操做,也叫猛劃,指用戶拖住UI元素快速滑動以後擡手,這時會有一個fling事件,通常的操做邏輯是UI元素在擡手以後按照初始速度作減速運動。
NestedScroll 接口也提供了API處理fling事件,在NestedScrollingChild
中dispatchNestedPreFling
通知NP預處理fling事件,dispatchNestedFling
通知NP後處理fling事件。
boolean dispatchNestedPreFling(float velocityX, float velocityY);
boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
複製代碼
NestedScrollingParent
中對應的接口爲onNestedPreFling
、onNestedFling
。
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時先收起上層視圖部份內容,再滑動下層視圖)。
爲了解決此問題,在support包 26.0.0-beta2
版本中引入了 NestedScroll 接口的升級版本(後面稱爲 NestedScroll++ ): NestedScrollingParent2
、NestedScrollingChild2
。
在 NestedScroll++ 接口中,引入了touch type 的概念:對於用戶手指觸摸拖拽產生的滑動事件type爲 ViewCompat.TYPE_TOUCH
, fling產生的滑動事件 type 爲 ViewCompat.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事件的處理,再也不依賴於 dispatchNestedPreFling
、dispatchNestedFling
、onNestedPreFling
、onNestedFling
等接口,而是選擇使用與滑動事件相同的處理方式,只是 type 不一樣(爲ViewCompat.TYPE_NON_TOUCH
)。
相應的交互邏輯改成:
在fling開始時,調用startNestedScroll
找到聯動這次滑動的NestedScrollingParent
(後面簡稱NP)。
每次刷新視圖時,計算當前時間片由fling產生的滑動距離,先調用dispatchNestedPreScroll
,詢問聯動NP是否預先處理此滑動距離,若是NP預先處理了,會給出消耗掉的滑動距離。
對於NP預處理剩下的滑動距離,NC決定本身是否處理部分或者所有距離(本身的滑動)。
若是NC本身滾動以後,還剩下部分滑動距離,則調用dispatchNestedScroll
讓NP自行選擇是否處理最後剩下的這些滑動距離。
用戶交互中止滑動,調用stopNestedScroll通知NC中止滑動聯動。
在onStartNestedScroll
中,決定是否與這次NC發起的fling聯動請求,若是決定聯動,返回 true
,不然返回 false
。返回true
以後,會收到onNestedScrollAccepted
回掉,表示NC贊成與其聯動,能夠開始作初始化操做了;返回false以後,後面的NC聯動操做不會通知此NestedScrollingParent
(不會收到後續的onNestedPreScroll
、onNestedScroll
、onStopNestedScroll
等)。
在onNestedPreScroll
中,決定是否預處理fling產生的滑動距離,並給出消耗掉的滑動距離(不處理則爲0)。
在onNestedScroll
中,決定是否消耗NC處理剩下的滑動距離。
在onStopNestedScroll
作聯動滑動收尾工做。