NestedScrolling 是谷歌推出的用于解决嵌套滑动的解决方案,在CoordinatorLayout中有使用。在 sdk api 21 之后,直接更新到了 View 和 ViewGroup源码里面,同时也在android.support.v4 包中提供了两个接口NestedScrollingChild和NestedScrollingParent, 还和两个辅助类 NestedScrollingChildHelper 和 NestedScrollingParentHelper 用来帮助开发者实现相关功能. 因此在 sdk21之后 系统原生控件是直接支持嵌套滑动的,如果自己需要实现,用那几个相关类就可以了。
原理
在Android触摸事件的处理中,无论是父类还是子类,一旦拦截了 Action_DOWN ,后续的事件都会发给这个控件,出于兼容的目的,这个流程不会大改,想要在滑动的过程中父类和子类联动,于是新增了接口方法,在 ACITION_MOVE 中进行调用,并且不改变整个onInterceptTouchEvent()和onTouchEvent()的返回值,这样既不会影响到原有流程,又可以让父类和子类进行交互。对于 Fling 的处理则是在 ACTION_UP 中,这个应该很好理解~
主要的过程则是子控件接收到滑动一段距离的请求时, 先询问父控件是否要滑动, 如果滑动了父控件就通知子控件它消耗了一部分滑动距离, 子控件就处理剩下的滑动距离, 然后子控件滑动完毕后再把剩余的滑动距离传给父控件.这个过程由子控件发起。
主要类和方法
NestedScrollingChild
- startNestedScroll : 起始方法, 主要作用是找到接收滑动距离信息的父控件.
- dispatchNestedPreScroll : 在内控件处理滑动前把滑动信息分发给父控件.
- dispatchNestedScroll : 在内控件处理完滑动后把剩下的滑动距离信息分发给父控件.
- stopNestedScroll : 结束方法, 主要作用就是清空嵌套滑动的相关状态
NestedScrollingChildHelper是对NestedScrollingChild的方法的实现
NestedScrollingParent
父控件接口主要是定义了一些响应子控件的方法,以onXXXXX命名和子控件的方法一一对应。
NestedScrollingParentHelper是对NestedScrollingParent的方法的实现
源码
1 | /** |
注释说的很清楚了。。
axes: SCROLL_AXIS_HORIZONTAL
和SCROLL_AXIS_VERTICAL
的值之一。
返回值 : 如果父控件可以响应嵌套滑动并且是enabled状态就返回true
在Action_DWON和onInterceptTouchEvent中调用,表示嵌套滑动的开始,从代码来看,做的仅仅是一直getParent(),如果父类的onStartNestedScroll()
返回true,就接着调用onNestedScrollAccepted()
进行初始化,否则继续向上寻找,没找到返回false。
后续应该调用 dispatchNestedPreScroll() ,如果它返回 true 则表示父控件至少消耗的部分或者全部的滑动距离
接着应该调用 dispatchNestedScroll() , 自己处理后再返回给父控件去处理。
1 | /** |
使用
上面说的断断续续,其实用起来很容易。
NestedScrollingChild
对于 NestedScrollingChild 来说,它需要做的是 :
- setNestedScrollingEnabled()设置为true
- 在ACTION_DOWN中调用 startNestedScroll()
- 在ACTION_MOVE中调用 dispatchNestedPreScroll() 和 dispatchNestedScroll()
- 在ACTOPN_UP|ACTION_CANCEL 中看情况调用 stopNestedScroll()
在sdk21之后,View类里面 onTouchEvent() 默认实现了这些步骤,如果不需要重写 onTouchEvent() 的话,本身就是支持这个功能的,如果重写的话,则需要自己看情况加入这些流程的调用。具体的例子可以看 RecyclerView 。作为support包的类,为了兼容性它自己实现了NestedScrollingChild接口,其实也就是调用 NestedScrollingChildHelper 类的相关方法,逻辑和sdk21之后的View的默认实现是类似的。
NestedScrollingParent
对于 NestedScrollingParent 来说,它需要做的就是 重写onXXXScroll()方法 ,这个根据不同的控件会有不同的效果,都需要自己去实现,对于sdk21之后的ViewGroup,提供了默认实现,就是直接调用 View的默认实现 dispatchNestedPreScroll() 和 dispatchNestedScroll() 继续向父控件分发,不满足条件则直接返回false,什么也不做。具体的例子可以看 ActionBarOverlayLayout 这个类,不过鉴于这个类不是很熟悉,可以看 ScrollView,SwipeRefreshLayout ,但这两个类都不仅仅可以作为 NestedScrollingParent,也可以作为NestedScrollingChild,看的时候不要弄混了。还有个 CoordinatorLayout,这个又做了一层封装,可以自己选择哪个类把。
后面有空可以自己实现一个来看看效果