NestedScrolling详解
简介
假设我们需要一个这样的效果,拖动子View的时候需要parent先滑动,等parent滑倒顶端的时候再让子View滑动。Android事件分发机制在parent处理事件的时候,没法再次把事件传递给子View(除非再来一个Down,开启一个新的事件序列),所以就需要用到NestedScrolling,也就是嵌套滑动机制。今天我们来实现如下效果
蓝色部分是子View,粉色是Parent,在向上滑动时,保证Parent首先滑动到顶端,向下滑动时保证子View首先滑倒底部。
基本类和方法
这里需要用到两个接口
1 | NestedScrollingChild |
和两个辅助类
1 | NestedScrollingChildHelper |
NestedScrollingChild
子View实现这个接口
1 |
|
- void setNestedScrollingEnabled(boolean enabled):允许嵌套滑动
- boolean startNestedScroll(int axes):一般在ACTION_DOWN的事件里调用,表示要开始滑动,axes代表方向,有SCROLL_AXIS_VERTICAL、SCROLL_AXIS_HORIZONTAL两种
- boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow):一般在ACTION_MOVE种调用,dx、dy是将要滑动的量,然后分发给Parent让他消耗,consumed是一个二维数组,分别存储Parent消耗的x、y方向上的量,如果无消耗那么返回false。
NestedScrollingParent
Parent实现这个接口
1 | public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes); |
- void onNestedPreScroll(View target, int dx, int dy, int[] consumed):子View调用dispatchNestedPreScroll的时候此方法会被回调,通过判断dx、dy来计算消耗,返回消耗值。
然而真正的逻辑实现都由Helper类帮我们实现了,我们只需要调用helper类的对应方法即可,接下来开始写代码。
ChildView代码
1 |
|
注释比较清楚了,主要就是方向不同逻辑不同,向上的时候先分发给Parent,如果Parent不消耗了(返回false,也就是说到达顶部了),那么自己消耗dy(向上滑动,注意越界情况);向下的时候,首先自己向下滑动(自己消耗dy),然后给Parent分发消耗后的dy。
ParentView代码
1 | /** |
比较简单,主要是注意越界的情况,接下来只要在布局文件里将ChildView设置为ParentView的child就可以了。
源码解析
但是这两者到底是怎么样联系起来的呢?我们看看Helper类的源码
1 | public boolean startNestedScroll(int axes) { |
startNestedScroll是最开始要调用的,作用就是把这个Child和Paren联系起来,内部首先寻找可用的Parent,然后回调Parent的onStartNestedScroll方法,如果返回true,那么就给内部的mNestedScrollingParent赋值同时回调Parent的onNestedScrollAccepted方法,否则mNestedScrollingParent还是null。如果已经有了Parent那么直接返回true,可以知道这个方法调用一次就可以了,只要和Parent联系起来就ok。
1 | public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { |
这个方法首先判断isNestedScrollingEnabled和mNestedScrollingParent,如果mNestedScrollingParent==null也就是Parent在onStartNestedScroll返回了false,那么就不会收到这个分发。方法内部回调了Parent的onNestedPreScroll方法,然后判断consume的两个值,如果都是0,那么说明Parent没有消耗,就返回false表示Parent不消耗。
总结
其实就是NestedScrollingChild发出各种事件,比如最开始的startNestedScroll来寻找可用的Parent同时回调Parent的方法,dispatchNestedPreScroll分发偏移量给Parent让它先消耗,而NestedScrollParent只是被动接受各种回调作出处理,比如在onStartNestedScroll返回boolean表示是否接受嵌套滑动,在onNestedPreScroll消耗滑动偏移量。其实高版本的View默认实现了这些方法,但是为了兼容低版本,我们是用Helper来实现,其实实现代码是一样的。