在MD系列的前几篇文章中,通过基础知识和实战案例配合讲解的形式介绍了CoordinatorLayout
与AppBarLayout
、Toolbar
、CollapsingToolbarLayout
的使用,并实现了几种MD风格下比较炫酷的交互效果。学会怎么用之后,我们再想想,为什么它们之间能够产生这样的交互行为呢?其实就是因为CoordinatorLayout.Behavior
的存在,这也是本文所要讲述的内容。至此,Android Material Design系列的学习已进行到第八篇,大家可以点击以下链接查看之前的文章:
- Android TabLayout 分分钟打造一个滑动标签页
- Android 一文告诉你到底是用Dialog,Snackbar,还是Toast
- Android FloatingActionButton 重要的操作不要太多,一个就好
- Android 初识AppBarLayout 和 CoordinatorLayout
- Android CoordinatorLayout实战案例学习《一》
- Android CoordinatorLayout 实战案例学习《二》
- Android 详细分析AppBarLayout的五种ScrollFlags
关于Behavior
官网对于CoordinatorLayout.Behavior
的介绍已经将它的作用说明得很清楚了,就是用来协调CoordinatorLayout
的Child Views之间的交互行为:
Interaction behavior plugin for child views of CoordinatorLayout.
A Behavior implements one or more interactions that a user can take on a child view. These interactions may include drags, swipes, flings, or any other gestures.
之前学习CoordinatorLayout
的使用案例时,用的都是系统的特定控件,比如design包中的FloatingActionButton
、AppBarLayout
等,而不是普通的控件,如ImageButton
之类的,就是因为design包中的这些特定控件已经被系统默认定义了继承自CoordinatorLayout.Behavior
的各种Behavior
,比如FloatingActionButton.Behavior
和AppBarLayout.Behavior
。而像系统的ToolBar
控件就没有自己的Behavior
,所以只能将其搁置到AppBarLayout
容器里才能产生相应的交互效果。
看到这里就能清楚一点了,如果我们想实现控件之间任意的交互效果,完全可以通过自定义Behavior
的方式达到。看到这里大家可能会有一个疑惑,就是CoordinatorLayout
如何获取Child Views的Behavior
的呢,为什么在布局中,有些滑动型控件定义了app:layout_behavior
属性而系统类似FloatingActionButton
的控件则不需要明确定义该属性呢?看完CoordinatorLayout.Behavior
的构造函数就明白了。
|
|
CoordinatorLayout.Behavior
有两个构造函数,注意看第二个带参数的构造函数的注释,里面提到,在这个构造函数中,Behavior
会解析控件的特殊布局属性,也就是通过parseBehavior
方法获取对应的Behavior
,从而协调Child Views之间的交互行为,可以在CoordinatorLayout
类中查看,具体源码如下:
|
|
parseBehavior
方法告诉我们,给Child Views设置Behavior
有两种方式:
app:layout_behavior
布局属性
在布局中设置,值为自定义Behavior
类的名字字符串(包含路径),类似在AndroidManifest.xml
中定义四大组件的名字一样,有两种写法,包含包名的全路径和以”.”开头的省略项目包名的路径。@CoordinatorLayout.DefaultBehavior
类注解
在需要使用Behavior
的控件源码定义中添加该注解,然后通过反射机制获取。这个方式就解决了我们前面产生的疑惑,系统的AppBarLayout
、FloatingActionButton
都采用了这种方式,所以无需在布局中重复设置。
看到这里,也告诉我们一点,在自定义Behavior
时,一定要重写第二个带参数的构造函数,否则这个Behavior
是不会起作用的。
根据CoordinatorLayout.Behavior
提供的方法,这里将自定义Behavior
分为两类来讲解,一种是dependent
机制,一种是nested
机制,对应着不同的使用场景。
dependent
机制
这种机制描述的是两个Child Views之间的绑定依赖关系,设置Behavior
属性的Child View跟随依赖对象Dependency View的大小位置改变而发生变化,对应需要实现的方法常见有两个:
|
|
和
|
|
具体含义在注释中已经很清楚了,layoutDependsOn()
方法用于决定是否产生依赖行为,onDependentViewChanged()
方法在依赖的控件发生大小或者位置变化时产生回调。dependent
机制最常见的案例就是FloatingActionButton
和SnackBar
的交互行为,效果如下:
系统的FloatingActionButton
已经默认定义了一个Behavior
来协调交互,如果不用系统的FAB控件,比如改用GitHub上的一个库futuresimple/android-floating-action-button
,再通过自定义一个Behavior
,也能很简单的实现与SnackBar
的协调效果:
|
|
很简单的一个自定义Behavior
处理,然后再为对应的Child View设置该属性即可。由于这里我们用的是第三方库,采用远程依赖的形式引入的,无法修改源码,所以不方便使用注解的方式为其设置Behavior
,所以在布局中为其设置,并且使用了省略包名的方式:
|
|
这样,采用dependent
机制自定义Behavior
,与使用系统FAB按钮一样,即可与SnackBar
控件产生如上图所示的协调交互效果。
比如我们再看一下这样一个效果:
列表上下滑动式,底部评论区域随着顶部Toolbar
的移动而移动,这里我们就可以自定义一个Dependent
机制的Behavior
,设置给底部视图,让其依赖于包裹Toolbar
的AppBarLayout
控件:
|
|
布局内容如下:
|
|
注意,这里将自定义的Behavior
设置给了底部内容的外层容器RelativeLayout
,即可实现上述效果。
Nested
机制
Nested
机制要求CoordinatorLayout
包含了一个实现了NestedScrollingChild
接口的滚动视图控件,比如v7包中的RecyclerView
,设置Behavior
属性的Child View会随着这个控件的滚动而发生变化,涉及到的方法有:
|
|
其中,onStartNestedScroll
方法返回一个boolean类型的值,只有返回true时才能让自定义的Behavior
接受滑动事件。同样的,举例说明一下。
通过查看系统FAB控件的源码可以知道,系统FAB定义的Behavior
能够处理两个交互,一个是与SnackBar
的位置交互,效果如上面的图示一样,另一个就是与AppBarLayout
的展示交互,都是使用的Dependent
机制,效果在之前的文章 – Android CoordinatorLayout 实战案例学习《二》 中可以查看,也就是AppBarLayout
滚动到一定程度时,FAB控件的动画隐藏与展示。下面我们使用Nested
机制自定义一个Behavior
,实现如下与列表协调交互的效果:
为了能够使用系统FAB控件提供的隐藏与显示的动画效果,这里直接继承了系统FAB控件的Behavior
:
|
|
然后在布局中添加RecyclerView
,并为系统FAB控件设置自定义的Behavior
,内容如下:
|
|
这样,即可实现系统FAB控件与列表滑动控件的交互效果。
@string/appbar_scrolling_view_behavior
这是一个系统字符串,值为:
|
|
在CoordinatorLayout
容器中,通常用在AppBarLayout
视图下面(不是里面)的内容控件中,比如上面的RecyclerView
,如果我们不给它添加这个Behavior
,Toolbar
将覆盖在列表上面,出现重叠部分,如图
添加之后,RecyclerView
将位于Toolbar
下面,类似在RelativeLayout
中设置了below
属性,如图:
示例源码
我在GitHub上建立了一个Repository,用来存放整个Android Material Design系列控件的学习案例,会伴随着文章逐渐更新完善,欢迎大家补充交流,Star地址:
https://github.com/Mike-bel/MDStudySamples