Android NestedScroll嵌套滑动机制解析
目录
Android提供了一个官方的嵌套滑动机制,让子View实现NestedScrollingChild
或者NestedScrollingChild2
接口,父布局实现NestedScrollingParent
或NestedScrollingParent2
接口,Android官方还提供了NestedScrollingChildHelper
和NestedScrollingChildHelper
两个帮助类,让开发者更容易实现嵌套滑动的逻辑.
最初的接口和接口2的区别
子View的接口有NestedScrollingChild
和NestedScrollingChild2
,NestedScrollingChild2
继承于NestedScrollingChild
,然后以多态的形式给大部分的方法都加了一个int类型的NestedScrollType
,这个int值是用来区分是用户触摸滑动还是其他滑动的,其共有两个类型
/**
* Indicates that the input type for the gesture is from a user touching the screen.
*/
public static final int TYPE_TOUCH = 0;
和
/**
* Indicates that the input type for the gesture is caused by something which is not a user
* touching a screen. This is usually from a fling which is settling.
*/
public static final int TYPE_NON_TOUCH = 1;
根据英文释义,TYPE_TOUCH
为用户触摸操作的类型,TYPE_NON_TOUCH
为非用户触摸操作类型,而且主要用于代码中的惯性操作,比如View滑动时的惯性滑动.
原始接口NestedScrollingChild
默认类型为TYPE_TOUCH
,如果需要实现子View和父View的惯性嵌套滑动则需要实现NestedScrollingChild2
接口
父View接口NestedScrollingParent
及NestedScrollingParent2
和子View一样在大部分方法中添加了NestedScrollType
,在此不做赘述.
接口方法介绍
在此只介绍原始接口的方法,对于扩展的第二接口由于只在原基础上加了一个类型,不多做介绍
NestedScrollingChild
public interface NestedScrollingChild {
/**
* 启用或者禁止嵌套滑动
*/
void setNestedScrollingEnabled(boolean enabled);
/**
* 用于判断嵌套滑动是否被启用
*/
boolean isNestedScrollingEnabled();
/**
* 开始嵌套滑动,参数为滑动方向,参数有如下几个
*
* 没有滑动方向
* public static final int SCROLL_AXIS_NONE = 0;
*
* 横向滑动
* public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
*
* 纵向滑动
* public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
*
* 其返回值代表父View是否接受嵌套滑动,如果不接受返回false,后续的嵌套滑动都将失效
*/
boolean startNestedScroll(@ScrollAxis int axes);
/**
* 是否有实现了NestedScrollingParent的父View
* 如果父View没有实现接口,此方法返回false,且所有嵌套滑动无效
*/
boolean hasNestedScrollingParent();
/**
* 分发嵌套滑动事件,在子View滑动处理完之后调用
*/
boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow);
/**
* 分发预嵌套滑动事件,在子View滑动处理之前调用
*/
boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
@Nullable int[] offsetInWindow);
/**
* 分发嵌套滑动的惯性滑动处理,返回值表示是否处理
*/
boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
/**
* 分发嵌套滑动的惯性滑动预处理,返回值表示是否处理,在子View处理之前调用
*/
boolean dispatchNestedPreFling(float velocityX, float velocityY);
}
NestedScrollingParent
此接口都是NestedScrollingChild
的接口回调,在子View接口方法被调用时便会调用父View的NestedScrollingParent
的方法,它们有着一一对应的关系,具体如下
NestedScrollingChild |
NestedScrollingParent |
备注 |
---|---|---|
startNestedScroll |
onStartNestedScroll |
前者的调用会触发后者的调用,然后后者的返回值将决定后续的嵌套滑动事件是否能传递给父View,如果返回false,父View将不处理嵌套滑动事件,一般前者的返回值即后者的返回值 |
onNestedScrollAccepted |
如果onStartNestedScroll 返回true,则回调此方法 |
|
stopNestedScroll |
onStopNestedScroll |
|
dispatchNestedScroll |
onNestedScroll |
|
dispatchNestedPreScroll |
onNestedPreScroll |
|
dispatchNestedFling |
onNestedFling |
|
dispatchNestedPreFling |
onNestedPreFling |
|
getNestedScrollAxes |
获得滑动方向,没有回调,为主动调用的方法 |
使用方法
子View接口的使用
子View接口常用实现
子View的接口通常都是借助NestedScrollingChildHelper
通过委派模式实现的,没有直接写在某个嵌套滑动子View里,提升了代码复用性,还是很高明的做法.
具体如下
在类中声明NestedScrollingChildHelper
对象
private final NestedScrollingChildHelper mChildHelper;
然后在子View构造函数中实例化
mChildHelper = new NestedScrollingChildHelper(this);
接下来实现NestedScrollingChild
接口
@Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}
@Override
public boolean isNestedScrollingEnabled() {
return mChildHelper.isNestedScrollingEnabled();
}
@Override
public boolean startNestedScroll(int axes) {
return mChildHelper.startNestedScroll(axes);
}
@Override
public void stopNestedScroll() {
mChildHelper.stopNestedScroll();
}
@Override
public boolean hasNestedScrollingParent() {
return mChildHelper.hasNestedScrollingParent();
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
offsetInWindow);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY,