源码看CoordinatorLayout.Behavior原理
来源:互联网 发布:mac怎么下载wps office 编辑:程序博客网 时间:2024/06/07 00:50
http://blog.csdn.net/qibin0506/article/details/50377592
在上一篇博客CoordinatorLayout高级用法-自定义Behavior中,我们介绍了如何去自定义一个CoordinatorLayout的Behavior,通过文章也可以看出Behavior在CoordinatorLayout中地位是相当高的,那么今天我们就来接着上篇博客来从源码分析一下Behavior的实现思路,如果你对CoordinatorLayout和Behavior还不熟悉的话,建议先去看看上篇博客《CoordinatorLayout高级用法-自定义Behavior》。
这篇文章我们要分析的内容有:
- Behavior的实例化
- layoutDependsOn和onDependentViewChanged调用过程
- onStartNestedScroll和onNestedPreScroll实现原理
- Behavior的事件分发过程
Behavior的实例化
大家都知道,我们在view中可以通过app:layout_behavior
然后指定一个字符串来表示使用哪个behavior,稍微去想一下,在CoordinatorLayout中肯定是利用反射机制来完成的behavior的实例化,现在就让我们从CoordinatorLayout的源码中找寻答案,来验证我们的猜想。首先,我们来看看CoordinatorLayout的一个内部类,也是大家熟悉的LayoutParams
,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在这里我们确实看到了behavior的影子,那它是在什么时候被初始化的呢?继续看代码,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
在LayoutParams的构造方法中,首先是去检查了是不是有layout_behavior
,这里很容易理解,接下来调用了parseBehavior
方法,返回了Behavior的实例,我们非常有理由去看看parseBehavior
到底干了嘛,或许我们要的答案就在里面!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
上面的代码很容易理解,就是利用反射机制去实例化了Behavior,调用的是两个参数的那个构造方法,这也就是我们在自定义Behavior的时候为什么一定要去重写,
- 1
- 2
- 3
- 1
- 2
- 3
这个构造的原因。看来获取一个Behavior的实例还是很简单的,那么,下面就让我们开始分析Behavior中常用方法调用的机制吧。
layoutDependsOn和onDependentViewChanged调用过程
在上一篇博客中我们学会了自定义两种形式的Behavior,其中第一种就是去观察一个view的状态变化,也就是涉及到layoutDependsOn
和onDependentViewChanged
两个方法的调用,现在我们从源码的角度来分析一下这两个方法调用的时机和调用的过程,在前一篇博客中我们提到过onDependentViewChanged
这个方法会在view的状态发生变化后去调用,那在状态发生变化时必定会执行什么操作呢?重绘,是的,状态变化了,那肯定重绘是避免不了的,在CoordinatorLayout
中注册了一个ViewTreeObserver
,我们可以从这里入手,因为它可以监听到view的各种状态变化,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
在onAttachedToWindow
向ViewTreeObserver注册了一个监听draw变化的Observer,那在这里Observer中到底干了嘛呢?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
就两行代码,调用了dispatchOnDependentViewChanged
方法,看方法名我们就知道这次找对对象了,怀着激动的心情来看看dispatchOnDependentViewChanged
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
dispatchOnDependentViewChanged
方法有一个布尔类型的参数,上面我们传递的是false, 这里主要是区分是view引起的状态变化还是布局引起的,在一些的scroll中也会调用dispatchOnDependentViewChanged
这个方法。
好了,现在我们终于搞懂了onDependentViewChanged
调用机制了,下面我们来看看关于滑动监听的部分。
onStartNestedScroll和onNestedPreScroll实现原理
在开始源码之前,我们先来思考个问题,现在有一个view是可以上下滑动的,那这个view的滑动对于父view来说是不是可见的?或者说是可预知的?显然不是,一个view的滑动对于父布局来说是透明的?所以现在我们不能简简单单的从CoordinatorLayout
入手了,而是要从那个可以滑动的view入手,我们选择NestedScrollView
来进行分析。NestedScrollView
有一个NestedScrollingChildHelper
类型的变量mChildHelper
引起了我们的注意,因为很多看名字很像关于滑动部分的代码都调用了这个类的一些方法,来看看有哪些吧?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
很简单,不过我们好像发现了一点眉目,这些方法何时调用我们还是不是很清楚,滑动必然和事件有关,我们就来从事件的部分入手吧,毕竟是我们熟悉的地方。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在down的时候我们调用了startNestedScroll
方法,那我们就顺着这条线往下看mChildHelper.startNestedScroll(axes)
。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
在这个方法中一个while循环,不断的去获取view的的parent,然后一个ViewParentCompat.onStartNestedScroll
作为条件成立了就return true了,我们有理由猜测ViewParentCompat.onStartNestedScroll
里去调用了CoordinatorLayout
的相应方法。注意参数,p是我们遍历到父view,我们先认为是CoordinatorLayout
吧,child是CoordinatorLayout
的直接嵌套着目标view的子view,mView在这里就是NestedScrollView
了。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
这里面很简单,看看parent是不是NestedScrollingParent
类型的,如果是,则调用了onStartNestedScroll
这个方法,而我们的CoordinatorLayout
肯定是实现了NestedScrollingParent
接口的,
- 1
- 1
好了,现在我们终于回到CoordinatorLayout
了,来看看他的onStartNestedScroll
方法,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
这里还是去遍历了所有子view,然后去调用它的onStartNestedScroll
方法,它的返回值,决定了NestedScrollingChildHelper.onStartNestedScroll
是不是要继续遍历,如果我们的子view对这个view的滑动感兴趣,就返回true,它的遍历就会结束掉。
好了,现在start的过程我们分析完了,大体的流程就是:
NestedScrollView.onInterceptTouchEvent->NestedScrollingChildHelper.onStartNestedScroll->CoordinatorLayout.onStartNestedScroll
下面的各种滑动调用流程也是一样的,这里我们就不再重复分析了,感兴趣的可以自己去看一下源码。
Behavior的事件分发过程
上面的分析其实已经将我们自定义Behavior中使用到的方法的调用流程分析完了,不过我们还是要拓展一下,其实Behavior也是支持事件的传递的,在这方面,Behavior好像是一个代理一样,在CoordinatorLayout的各种事件处理的方法中去调用Behavior的事件处理方法,返回值决定了CoordinatorLayout对事件的消费情况。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
这里面调用了performIntercept
方法,而且指定了个常量TYPE_ON_INTERCEPT
代表了我们在拦截阶段调用的,既然有区分,肯定在别的地方也有调用,答案是肯定的,在onTouch
里也有对performIntercept
的调用,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
恩,这里面的代码注释已经写的很明白了,但是需要注意的一点,这一点我很长时间没有相通,就是为什么还要在onTouch
里还要调用一遍performIntercept
,是这样的,假如现在事件没有任何子view去消费,那么事件会冒泡到此,本着把Behavior看作是一个代理的原则,这里肯定还是要去询问一下Behavior是不是要执行这个事件,注意这里说的是执行而不是拦截,这是因为performIntercept
不仅仅会调用Behavior的拦截部分的代码,也会调用执行的代码,就是通过第二个参数区分的。可以看到,这里我们使用了TYPE_ON_TOUCH
。
好了,说了这么多performIntercept
,是时候来看看performIntercept
的代码了。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
这里面的代码也很容易理解,就是去遍历所有的view,在不同的情景下调用Behavior的onInterceptTouchEvent或onTouch方法。
好了关于Behavior的源码我们就分析到这里,相信大家在看完之后会对Behavior有一个全新的认识,而且google已经建议我们使用support design的东西了(没发现现在的项目默认模板文件就是一个标准的support design布局吗),所以我们还是有必要对新东西有个更加深入的认识,而且这样也会有助于我们理解google工程师的思路,在解决一些问题的时候我们完全可以参考一下这些思路。
ok,不扯了,今天就到这里吧,拜拜。
提问:
final View view = getChildAt(i);
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final Behavior viewBehavior = lp.getBehavior();
这是View的类型为什么为:AppBarLayout而不是设备NestedScrollView
回答:
这里的类型确实是AppBarLayout
CoordinatorLayout-->onMeasure-->prepareChildren-->getResolvedLayoutParams
在getResolvedLayoutParams中通过注解给AppBarLayout设置了Behavior.
而CoordinatorLayout中onStartNestedScroll的View view = getChildAt(0);就是AppBarLayout。
- 源码看CoordinatorLayout.Behavior原理
- 源码看CoordinatorLayout.Behavior原理
- CoordinatorLayout自定义Behavior&源码分析
- CoordinatorLayout与Behavior源码分析
- CoordinatorLayout源码解析之初识Behavior
- CoordinatorLayout behavior
- CoordinatorLayout.Behavior
- CoordinatorLayout源码解析,探索Behavior机制的奥秘
- Android CoordinatorLayout和Behavior的源码分析(一)
- Android CoordinatorLayout和Behavior的源码分析(二)
- Android CoordinatorLayout和Behavior的源码分析(三)
- Android CoordinatorLayout和Behavior的源码分析(四)
- CoordinatorLayout调用原理源码解析
- Tablayout,CoordinatorLayout与Behavior
- 深入理解CoordinatorLayout.Behavior
- CoordinatorLayout与Behavior
- CoordinatorLayout与Behavior总结
- 自定义实现CoordinatorLayout.Behavior
- rsyslog+mariadb+loganalyzer实现日志服务器搭建
- 把一个整数每个数位上的数累加,然后输出
- 使用Dev-C++查看vector数组中的变量值
- [CF555E]Case of Computer Network/[51NOD1470]计算机网络问题
- Java源代码阅读——Object类
- 源码看CoordinatorLayout.Behavior原理
- 用homebrew安装protobuf提示找不到包的解决办法
- 上车问题
- 全面了解 Nginx 主要应用场景
- Python访问MySQL数据库并实现其增删改查功能
- js闭包是什么?
- JAVA中this用法小结
- Nginx 日志分析及性能排查
- PHP 排序