前一篇文章中分析了解决滑动冲突问题的 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
作联动滑动收尾工做。