NestedScrolling(嵌套滑动)机制
传统的事件分发:
由父View发起,一旦父View需要自己做滑动效果就要拦截掉事件并通过自己的onTouch进行消耗,这样子View就再没有机会接手此事件,如果自己不拦截交给子View消耗,那么不使用特殊手段的话父View也没法再处理此事件。
NestedScrolling机制:
NestedScrolling就是父View和子View之间的一套滑动交互机制。简单点来说就是要求子View在准备滑动之前将滑动的细节信息传递给父View,父View可以决定是否部分或者全部消耗掉这次滑动,并使用消耗掉的值在子View滑动之前做自己想做的事情,子View会在父View处理完后收到剩余的没有被父View消耗掉的值,然后再根据这个值进行滑动。滑动完成之后如果子View没有完全消耗掉这个剩余的值就再告知一下父View。
具体的类:
父控件需要实现的接口与使用到的类:
NestedScrollingParent(接口)
NestedScrollingParent2(也是接口并继承NestedScrollingParent)
NestedScrollingParentHelper(类)
子控件需要实现的接口与使用到的类:
NestedScrollingChild(接口)
NestedScrollingChild2(也是接口并继承NestedScrollingChild)
NestedScrollingChildHelper(类)
NestedScrollingParent
/**
* 有嵌套滑动到来了,判断父控件是否接受嵌套滑动
*
* @param child 嵌套滑动对应的父类的子类(因为嵌套滑动对于的父控件不一定是一级就能找到的,可能挑了两级父控件的父控件,child的辈分>=target)
* @param target 具体嵌套滑动的那个子类
* @param nestedScrollAxes 支持嵌套滚动轴。水平方向,垂直方向,或者不指定
* @return 父控件是否接受嵌套滑动, 只有接受了才会执行剩下的嵌套滑动方法
*/
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {}
/**
* 当onStartNestedScroll返回为true时,也就是父控件接受嵌套滑动时,该方法才会调用
*/
public void onNestedScrollAccepted(View child, View target, int axes) {}
/**
* 在嵌套滑动的子控件未滑动之前,判断父控件是否优先与子控件处理(也就是父控件可以先消耗,然后给子控件消耗)
*
* @param target 具体嵌套滑动的那个子类
* @param dx 水平方向嵌套滑动的子控件想要变化的距离 dx<0 向右滑动 dx>0 向左滑动
* @param dy 垂直方向嵌套滑动的子控件想要变化的距离 dy<0 向下滑动 dy>0 向上滑动
* @param consumed 这个参数要我们在实现这个函数的时候指定,回头告诉子控件当前父控件消耗的距离
* consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离 好让子控件做出相应的调整
*/
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {}
/**
* 嵌套滑动的子控件在滑动之后,判断父控件是否继续处理(也就是父消耗一定距离后,子再消耗,最后判断父消耗不)
*
* @param target 具体嵌套滑动的那个子类
* @param dxConsumed 水平方向嵌套滑动的子控件滑动的距离(消耗的距离)
* @param dyConsumed 垂直方向嵌套滑动的子控件滑动的距离(消耗的距离)
* @param dxUnconsumed 水平方向嵌套滑动的子控件未滑动的距离(未消耗的距离)
* @param dyUnconsumed 垂直方向嵌套滑动的子控件未滑动的距离(未消耗的距离)
*/
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {}
/**
* 嵌套滑动结束
*/
public void onStopNestedScroll(View child) {}
/**
* 当子控件产生fling滑动时,判断父控件是否处拦截fling,如果父控件处理了fling,那子控件就没有办法处理fling了。
*
* @param target 具体嵌套滑动的那个子类
* @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
* @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
* @return 父控件是否拦截该fling
*/
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {}
/**
* 当父控件不拦截该fling,那么子控件会将fling传入父控件
*
* @param target 具体嵌套滑动的那个子类
* @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
* @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
* @param consumed 子控件是否可以消耗该fling,也可以说是子控件是否消耗掉了该fling
* @return 父控件是否消耗了该fling
*/
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {}
/**
* 返回当前父控件嵌套滑动的方向,分为水平方向与,垂直方法,或者不变
*/
public int getNestedScrollAxes() {}
NestedScrollingChild
/**
* 开启一个嵌套滑动
*
* @param axes 支持的嵌套滑动方法,分为水平方向,竖直方向,或不指定
* @return 如果返回true, 表示当前子控件已经找了一起嵌套滑动的view
*/
public boolean startNestedScroll(int axes) {}
/**
* 在子控件滑动前,将事件分发给父控件,由父控件判断消耗多少
*
* @param dx 水平方向嵌套滑动的子控件想要变化的距离 dx<0 向右滑动 dx>0 向左滑动
* @param dy 垂直方向嵌套滑动的子控件想要变化的距离 dy<0 向下滑动 dy>0 向上滑动
* @param consumed 子控件传给父控件数组,用于存储父控件水平与竖直方向上消耗的距离,consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离
* @param offsetInWindow 子控件在当前window的偏移量
* @return 如果返回true, 表示父控件已经消耗了
*/
public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow) {}
/**
* 当父控件消耗事件后,子控件处理后,又继续将事件分发给父控件,由父控件判断是否消耗剩下的距离。
*
* @param dxConsumed 水平方向嵌套滑动的子控件滑动的距离(消耗的距离)
* @param dyConsumed 垂直方向嵌套滑动的子控件滑动的距离(消耗的距离)
* @param dxUnconsumed 水平方向嵌套滑动的子控件未滑动的距离(未消耗的距离)
* @param dyUnconsumed 垂直方向嵌套滑动的子控件未滑动的距离(未消耗的距离)
* @param offsetInWindow 子控件在当前window的偏移量
* @return 如果返回true, 表示父控件又继续消耗了
*/
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) {}
/**
* 子控件停止嵌套滑动
*/
public void stopNestedScroll() {}
/**
* 当子控件产生fling滑动时,判断父控件是否处拦截fling,如果父控件处理了fling,那子控件就没有办法处理fling了。
*
* @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
* @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
* @return 如果返回true, 表示父控件拦截了fling
*/
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {}
/**
* 当父控件不拦截子控件的fling,那么子控件会调用该方法将fling,传给父控件进行处理
*
* @param velocityX 水平方向上的速度 velocityX > 0 向左滑动,反之向右滑动
* @param velocityY 竖直方向上的速度 velocityY > 0 向上滑动,反之向下滑动
* @param consumed 子控件是否可以消耗该fling,也可以说是子控件是否消耗掉了该fling
* @return 父控件是否消耗了该fling
*/
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {}
/**
* 设置当前子控件是否支持嵌套滑动,如果不支持,那么父控件是不能够响应嵌套滑动的
*
* @param enabled true 支持
*/
public void setNestedScrollingEnabled(boolean enabled) {}
/**
* 当前子控件是否支持嵌套滑动
*/
public boolean isNestedScrollingEnabled() {}
/**
* 判断当前子控件是否拥有嵌套滑动的父控件
*/
public boolean hasNestedScrollingParent() {}
其调用的相关的调用关系如下图所示,
![](https://img.haomeiwen.com/i8086176/d42a0777d871e41f.png)
现在先看框部分的,后半部分将在下篇进行分析。
NestedScrollingChild2与NestedScrollingParent2这两个接口,主要是处理fling事件,子控件就能将fling传递给父控件,并且父控件处理了部分fling后,又可以将剩余的fling再传递给子控件。当子控件停止fling时,通知父控件fling结束了。