嵌套滑动与View生命周期 - NestedScrollView

2,780 阅读4分钟

本文得配合 :
事件分发浅析 - 仿京东首页二级联动 + 事件分发详解 食用。

事件分发与问题

事件分发详解: juejin.cn/post/697614…

事件分发,是自上而下的:activity -> viewgroup -> view
事件消费,是从下往上的:view -> viewgroup -> activity

问题:
当需要实现这样的嵌套滑动的时候。事件冲突常规解决方案有两种:内部拦截、外部拦截。
但是不管什么样的解决方案,(当头部还有能滑动的时候,父亲消费,让头部先滑动。否则儿子消费,下面滑动)。
都不能解决一次滑动事件(一个Down - 多个Move,前面的Move滑父亲。但是后面的Move父亲滑完了,得滑儿子),即让父亲消费又让儿子消费。(也就是滑到头,得抬手在滑动一次,才能滑动儿子)

444.gif

NestedScrollView

因为无法将一次事件中的Move,即分发给父亲,又分发给儿子。这样的嵌套滑动,谷歌在Material Design给了一个NestedScrollView来解决嵌套滑动。

NestedScrollView 分为 NestedScrollingParent 和 NestedScrollingChild

得同时实现NestedScrollingParent和NestedScrollingChild才能嵌套滑动

在需要嵌套滑动的地方,如果解析到实现了儿子NestedScrollingChild的控件,会向上遍历父亲树,直到遍历到实现了父亲NestedScrollingParent的控件,然后绑定(还会继续向上找,因为可能有多层嵌套)。如果没找到父亲,那就不支持嵌套滑动。

在处理滚动事件的时候,都是由儿子发起请求,然后父亲接收,所以儿子都是dispatch、start这样的,父亲都是onXXX

总结:孔融让梨
孔融有一个梨(事件),可孔融比较孝顺,所以:
1.孔融 dispatchNestedPreScroll 询问父亲吃不吃 --> 父亲:1.吃完(事件结束) 2. 咬一口,还有剩余
2.还有剩余,孔融再吃 1.吃完(事件结束) 2. 咬一口,还有剩余
3.还有剩余,孔融 dispatchNestedScroll 询问父亲吃不吃 --> 父亲:1.吃完(事件结束) 2. 咬一口,还有剩余
4.还有剩余孔融吃完。

image.png

初始化阶段:儿子 StartNestedScroll 向上寻找父亲并绑定。(起点其实是儿子的onTouchEvent的Down)
预滚动阶段:儿子boolean dispatchNestedPreScroll第一次询问父亲是否消费,父亲onNestedPreScroll接收处理事件 (起点其实是儿子的onTouchEvent的Move)
滚动阶段:儿子boolean dispatchNestedScroll第二次询问父亲是否消费,父亲onNestedScroll接收处理事件

NestedScrollingChild

NestedScrollingChild与NestedScrollingParent是接口interface。

注释已经很清楚了。
image.png

NestedScrollingChild 与 NestedScrollingChild2

差别就是NestedScrollingChild2多了一个int type。这个Type代表滑动的类型,一共有两种类型,Touch和fling 手滑的还是惯性

image.png

NestedScrollingParent

NestedScrollingParent没啥好说的,跟Child一样

image.png

NestedScrollingChildHelper 与 NestedScrollingParentHelper

帮助类,开始和结束可以直接调用helper里的方法,省的自己写。(StartNestedScroll、onStopNestedScroll等等)
自己写的主要就是中间 预滚动阶段、滚动阶段(dispatchNestedPreScroll、onNestedPreScroll等)。

自己不实现的方法全丢给NestedScrollingChildHelper,然后只实现自己需要的
image.png image.png

image.png

CoordinatorLayout协调者布局

当需要处理子控件与子控件之间产生相互关系的时候,可以使用CoordinatorLayout。(拖动一个控件,另一个控件颜色改变,位置改变等等。)
他是NestedScrollView的进阶,NestedScrollView只管滑动,而且是父控件与子控件之间的关系。但是CoordinatorLayout可以子控件与子控件之间产生关系,主要有下面四个功能。

1. 处理子控件之间依赖下的交互
2. 处理子控件之间的嵌套滑动
3. 处理子控件的测量与布局
4. 处理子控件的事件拦截与响应

这些功能都建立在Behavior插件的基础上。

主要有下面这些方法
image.png

Behavior插件

Behavior插件是需要什么功能就插入什么功能的,像插件化。这样省空间。

其中的原理其实就是观察者模式,在depandency控件里存有订阅的List child,当被观察者发生改变的时候,通过判断依赖 (layoutDependsOn),然后发消息,这个是child的接收方法(onDependentViewChanged/onDepe ndentViewRemoved)。

image.png

在嵌套滑动里CoordainatorLayout还能给ChildView发送消息。
在CoordainatorLayout下的子控件可以与多个兄弟控件进行交互。 image.png

子控件的onMeasureChild与onLayoutChild方法 image.png

View的生命周期(重点)

View的生命周期 oncreate -> setcontentView(layout) LayoutInflater.inflate xml -> view onFinishInflater add content DecorView

activity ->window ->view

attach 的时候 new PhoneWindow

resume里有 makevisible方法, PhoneWindow.addview(DecorView)

setview -> wms(WindowsManagerService) -> surfacefling->skia(底层图形绘制库)

ViewRootImpl(View与wms的桥梁): view 刷新机制里有: performTraversals measure -> layout -> draw

activity onCreate

[改变可见性] --> 构造View() --> onFinishInflate() --> onAttachedToWindow() --> onMeasure() --> onSizeChanged() --> onLayout() --> onDraw() --> onDetackedFromWindow()

image.png