5.0支持库嵌套滚动机制分析

google在V21里面加入了几个兼容类,CoordinatorLayout,NestedScrollView,recyclerview都具有嵌套滚动的机制。

他们的效果如下图:

上面是CoordinatorLayout和NestedScrollView,其中CoordinatorLayout实现了NestedScrollingParent接口,其余两个实现了NestedScrollingChild接口,在NestedScrollView执行触摸操作的时候,会回调给CoordinatorLayout。

那么开始时看这套机制代码前,我们先了解什么是嵌套滚动呢,举个例子:

你问你父亲要了100块钱,然后你准备出去买一个冰激凌吃,由于你是一个好孩子,于是你在去买冰激凌的时候告诉你父亲,然后你父亲觉得一个冰激凌要不到100块,于是他就把你的100块拿了70块去,然后重新给你了30块,于是你花了20块买了一个冰激凌剩余的10元自己买了点其他零食,由于你是个好孩子,你回去之后告诉了你的父亲买了什么。

是不是一脸懵逼

那么嵌套滚动对应上面的例子方法就是:你就是子view 你支持嵌套滚动,100块是触摸的值,要执行买冰激凌前你告诉你了父亲,你父亲可以根据决定是否要扣留你的这100块钱,如果扣完了你就没法干啥事情了,如果没有扣完,于是把你剩下的给你,相当于通过回调,子view在滚动前会通知父view,然后父view可以通过逻辑来决定是否消耗触摸值,如果消耗那么可以执行相应的操作,如果不消耗子view继续执行它自己的逻辑和操作。

先从NestedScrollingParent,NestedScrollingChild接口开始看

看下这个两个接口的方法定义:

  • NestedScrollingParent

onStartNestedScroll:

如果当前布局支持滚动,则在子view ACTION_DOWN 时会回调此方法,通知父亲表示嵌套滚动开始

onNestedScrollAccepted:

如果接受滚动则可以在滚动前做一些初始化操作

onStopNestedScroll:

在一次嵌套滚动完成了,此方法会调用,在子view ACTION_UP或ACTION_CANCEL 时会回调此方法

onNestedScroll:

在子view滚动中的时候,调用

onNestedPreScroll:

子view有触摸的值,在滚动之前会回调给父亲,父亲根据需要消费

onNestedFling:

子view正在处理抛的操作

onNestedPreFling:

子view有抛的值,在滚动之前会回调给父亲,父亲根据需要消费

getNestedScrollAxes:

滚动的方向

  • NestedScrollingParentChild

setNestedScrollingEnabled:

激活嵌套滚动操作

isNestedScrollingEnabled:

是否已经激活嵌套滚动操作

startNestedScroll:

通知父亲滚动开始

stopNestedScroll:

通知父亲本次滚动完成

hasNestedScrollingParent:

判断是否有支持嵌套滚动的父亲

dispatchNestedScroll:

分发滚动值,会回调给给父亲的onNestedScroll

dispatchNestedPreScroll:

滚动之前分发滚动值,会回调给给父亲的onNestedPreScroll

dispatchNestedFling:

分发抛的值,会回调给给父亲的onNestedFling

dispatchNestedPreFling:

抛之前分发滚动值,会回调给给父亲的onNestedPreFlin

上面是两个接口方法,大致描述。

另外还有两个帮助处理回调逻辑的兼容类(API21之前)NestedScrollingChildHelper,NestedScrollingParentHelper在执行一些view相关操作的时候这两个类会用ViewCompat,ViewParentCompat这两个兼容视图,达到在早于5.0之前或之后可以嵌套滚动互操作性,具体怎么用后面会讲到

接下来看下CoordinatorLayout 和 NestedScrollView它们之间是怎么进行交互的,首先NestedScrollView是CoordinatorLayout的孩子,当我们手指触摸屏幕的时候,事件会到NestedScrollView里,这个时候NestedScrollView就会根据触摸的值来回调给CoordinatorLayout。

注意CoordinatorLayout在不设置Behavior的情况下是默认不拦截事件了的,看一下代码

会调用performIntercept方法判断是否需要拦截

这个方法里面首先对子view进行排序,然后获取是否设置了behavior(后面再说这是个什么东西),如果设置了会调用behavior里面的代理方法。

接着当我们手指触摸屏幕,事件分发到NestedScorllView的时候,那么整个嵌套滚动操作就开始了

首先NestedScorllView会实例化两个帮助类,NestedScrollingParentHelper和NestedScrollingChildHelper,然后激活嵌套滚动

接着在事件传递给NestedScorllView,并且它自己处理的时候,在onTouchEvent DOWN会调用startNestedScroll:

点进去startNestedScroll看:

最终是在NestedScrollingChildHelper帮助类里面调用的,继续看帮助类里面的startNestedScroll:

首先判断是否能嵌套滚动,然后判断是否激动了滚动,然后在循环找支持嵌套滚动的父亲,如果当前支持滚动就调用滚动开始方法,并且返回,这里用的兼容视图ViewParentCompat里面方法startNestedScroll:

其实本质上就是调用了父亲的onStartNestedScroll,我们这里就是CoordinatorLayout的onStartNestedScroll了

然后接着,就是滚动操作了,接下来就要到ACTION_MOVE 方法体里面去了:

首先会计算偏移量,然后调用dispatchNestedPreScroll,这个方法其实就是调用了CoordinatorLayout的OnNestedPreScroll表示我要开始滚动了,我先发给父亲看父亲有没有消费这次的滚动值,点进去方法可以看到其实调用的也是帮助类的,那么最终我去帮助类里面看看方法的实现:

首先判断判断是否可以嵌套滚动,获取偏移量,然后就最终调用了父亲的onNestedPreScroll了,在代码的300行,这个时候我们就可以在父亲的回调里面根据页面逻辑处理偏移量了,是否消费或者不消费,consumed[0]代表X轴,consumed[1]代表Y轴。

然后接着代码会根据dispatchNestedPreScorll的返回值,计算还有多少偏移量没有被消费,接着进入到下面的if代码体,如果不是拖动状态,且剩余的偏移量大于最小触摸偏移量,就会进入到代码体里面,然后就进入到拖动状态,接着代码下就会调用dispatchNestedScroll,最终会调用父亲的onNestedScroll:

上面的234行会回调父亲的onNestedScroll告诉父亲我现在还要滚动这些偏移量,到这儿整个move的就完了

接着在ACTION_UP代码体里面会处理dispatchNestedPreFling和dispatchNestedFling最终也会回调给父亲,也和上面一样可以选择消费或者不消费:

最后还会调用stopNestedScroll,并且释放和回收资源,到此整体机制就结束了

结合上面的代码,我们知道我们可以在父亲的回调方法里面选择消费或者不消费,如果消费完了子view 不会执行任何操作,如果没消费完子view还会消费剩余的,我们可以在父亲的相对于的回调方法里面做一些页面上的滚动啊,放大缩小等。

可以说这套机制也是建立在事件分发基础上的,但是它比事件分发更灵活,我们只需要实现NestedScrollingParent和NestedScrollingChild,然后只需要关心OnNestedPreScroll,OnNestedScroll,OnNestedPreFling,OnNestedfling ,其余的可以交给帮助代理类NestedScrollingChildHelper,NestedScrollingParentHelper我们只需要了解原理即可。

后面需用这套机制去自定义一个类似于最上面GIF图上面那样的效果

最后还有一个beahvior,后面记录

转载于:https://my.oschina.net/xwbbwx/blog/2988624

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值