实现嵌套滑动——NestedScrollingParent与NestedScrollingChild接口及其实现

什么是嵌套滑动

嵌套滑动示例
这是某个外卖App的点餐界面,它实现了这样一种滑动效果:

  • 当顶部标签未完全隐藏时,滑动的是整个ViewGroup。
  • 当顶部标签完全隐藏时,滑动的是列表内容。

按照正常的事件分发机制,你可能会想利用onInterceptTouchEvent()方法,在顶部标签未完全隐藏时由ViewGroup将触摸事件拦截,在顶部标签完全隐藏时将触摸事件分发给子view来解决这个问题。但事实上这是不可能的。考虑这样一种情况:在ACTION_DOWN时顶部标签还未完全隐藏,此时ViewGroup通过onInterceptTouchEvent()拦截了触摸事件。如果这样做了,子View就完全没有处理ACTION_DOWN的机会,也就无法收到后续的任何触摸事件,自然也就不可能实现滚动子view的内容的效果。
为解决这一问题,Google在support.v4包中提供了两个接口,即标题中的NestedScrollingParent与NestedScrollingChild。从名字上就可以看出,NestedScrollingParent是提供给ViewGroup实现的,NestedScrollingChild是提供给View实现的。同时,为了简化实现这两个接口的工作,support.v4包中同时还提供了NestedScrollingParentHelper与NestedScrollingChildHelper两个帮助类,里面的方法和接口中对应的方法同名。本文的主要内容就是分析这两个接口与两个帮助类的源码,弄清楚它们的内部逻辑,为实现嵌套滑动打好基础。

NestedScrollingParent

首先看看提供给ViewGroup实现的NestedScrollingParent中包含的方法:

public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);

在子view发出嵌套滑动请求时被调用。
child:这个ViewGroup的含有target的直接子view;
target:发出嵌套滑动请求的view;
nestedScrollAxes:指明滑动方向,可能为ViewCompat.SCROLL_AXIS_HORIZONTAL(水平滑动)或是
ViewCompat.SCROLL_AXIS_VERTICAL(垂直滑动)。
返回值:true代表同意进行嵌套滑动。

public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);

在同意了子view的嵌套滑动请求后被调用,也就是onStartNestedScroll()返回true之后。
参数同onStartNestedScroll()。

public void onStopNestedScroll(View target);

嵌套滑动结束后被调用。
target:发起嵌套滑动的view。

public void onNestedScroll(View target, int dxConsumed, int dyConsumed,int dxUnconsumed, int dyUnconsumed);

嵌套滑动过程中被调用,此时target已经消费掉部分滑动。
target:发起嵌套滑动的view;
dxConsumed:被target消费掉的水平滑动;
dyConsumed:被target消费掉的垂直滑动;
dxUnconsumed:未被target消费掉的水平滑动;
dyUnconsumed:未被target消费掉的垂直滑动。

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);

在target消费滑动之前被调用。
target:发起嵌套滑动的view;
dx:水平滑动距离;
dy:垂直滑动距离;
consumed:被消费掉的滑动距离。注意这个参数是用作输出的,在执行完具体的滑动逻辑之后,将实际消费掉的水平滑动距离与垂直滑动距离填在consumed[0]与consumed[1]中。

public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);

在子view监测到一次抛动后被调用。
target:发起嵌套滑动的view;
velocityX:水平速度;
velocityY:垂直速度;
consumed:target是否消费了这次抛动;
返回值:true代表父view对这次抛动作出了响应。

public boolean onNestedPreFling(View target, float velocityX, float velocityY);

和前面的相似,不过这个是在target消费抛动之前。

public int getNestedScrollAxes();

返回本次嵌套滑动的方向。
可以发现,NestedScrollingParent里面基本都是一些回调方法。接下来看看NestedScrollingParentHelper能帮我们做些什么:

private int mNestedScrollAxes;

public void onNestedScrollAccepted(View child, View target, int axes) {
    mNestedScrollAxes = axes;
}

public int getNestedScrollAxes() {
    return mNestedScrollAxes;
}

public void onStopNestedScroll(View target) {
    mNestedScrollAxes = 0;
}

好吧,它基本上做不了什么,只是帮忙管理了关于滑动方向的信息而已。实际上这也是可以理解的,因为只有这样,才能够根据实际需要作出不同的响应,这也是回调方法存在的最大意义。
从前面介绍的方法中也能看出,嵌套滑动是由子view发起的,主动权握在子view手上,父view仅仅是在特定的时间点作出响应而已。接下来就来看看NestedScrollingChild接口提供了些什么方法。

NestedScrollingChild

不同于NestedScrollingParentHelper,NestedScrollingChildHelper实现了NestedScrollingChild接口中的所有方法。也就是说,当我们需要实现NestedScrollingChild接口时,只需要在对象内部创建一个NestedScrollingChildHelper,然后把所有方法的实现都交给它就行了。因此,我们直接从NestedScrollingChildHelper入手,看看它提供了些什么方法,以及它们是如何实现的。

public void setNestedScrollingEnabled(boolean enabled) {
    if (mIsNestedScrollingEnabled) {
        //停止当前滑动
        ViewCompat.stopNestedScroll(mView);
    }
    mIsNestedScrollingEnabled = enabled;
}

public boolean isNestedScrollingEnabled() {
    return mIsNestedScrollingEnabled;
}

设置是否允许启动嵌套滑动,以及当前是否允许嵌套滑动。


                
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值