在2015年谷歌开发中大会上,谷歌推出了全新的设计语言--Material Design。在发布的众多新控件中,最有意思的一个要数更强大的FrameLayout--CoordinatorLayout。CoordinatorLayout实现了多种Material Design中提到的滚动效果,让你不用写动画代码就能动起来,这些效果包括:
- 让FloatingActionButton上下滑动,为Snackbar留出空间。
- 扩展、收缩Toolbar或者头部,让主内容区域有更多的空间。
- 控制某个view扩展还是收缩,以及其显示大小、比例等,包括视差滚动效果动画。
- ......
来欣赏一下facebook的效果
facebook万变不离其宗,我们从基本入手,一步步来掌握CoordinatorLayout
初探
使用CoordinatorLayout需要在Gradle加入Support Design Library:
compile 'com.android.support:appcompat-v7:24.2.1'
我们来看看最基本的布局效果
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/main_appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_toolbar"
app:title="@string/app_name"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
....
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
这里先不说源码,我直接对布局中一些新概念简单的说明下,
- CoordinatorLayout作为最顶层视图,将旗下所有的子view进行调度管理,以达到相互依赖的关系。这个关系是通过Behavior发起的,包括了滑动状态的处理以及View状态(大小、位置等属性)的处理
- AppBarLayout其实就是一个垂直的线性布局,跟线性布局相比他实现了很多Material Design效果中Bar的功能,即滑动手势
- Behavior是Design库中新诞生的一个布局概念,让你有机会以非侵入式的方式去处理这个View如何和其他View的交互行为。Behavior需要设置在触发事件(比如滚动)的view上,且这个View必须是CoordinatorLayout的第一层级下的子view,否则没有效果,因为Behavior的初始化是在CoordinatorLayout的LayoutParams中通过反射完成的,如果你把值写在LinearLayout中,那么就不行了。
Behavior实例化既可以像上面提到的用反射将一个类的路径通过app:layout_behavior声明,比如这里appbar_scrolling_view_behavior的值是android.support.design.widget.AppBarLayout$ScrollingViewBehavior,也就是AppBarLayout的内部类ScrollingViewBehavior,同时也可以在你的自定义View类上添加@DefaultBehavior(你的Behavior.class)这句注解,就像AppBarLayout一样
@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
- Behavior本身是一个接口而已,调度过程其实是通过NestedScrollingParent与NestedScrollingChild这2个接口完成的,像CoordinatorLayout实现了NestedScrollingParent,RecyclerView或者NestedScrollView实现了NestedScrollingChild接口。有些原生的View好比ListView是没有实现这个接口的,如果想让它拥有滑动嵌套功能,那就必须去设置嵌套滑动条件允许,即设置setNestedScrollingEnabled为true。
顺带简单说一下流程,因为已经实现了嵌套滚动,所以NestedScrollView的滚动影响着CoordinatorLayout,CoordinatorLayout就会遍历去找所有第一层Behavior。这里找到了AppBarLayout.Behavior,然后AppBarLayout就滚动起来了,顺带着通过appbar_scrolling_view_behavior把NestedScrollView也移动起来 - 一般我们看到的文章都是描述CoordinatorLayout跟AppBarLayout还有ViewPager什么的,其实任何View只需使用Behavior配置好关系,都可以在CoordinatorLayout中实现嵌套滚动这个概念,跟AppBarLayout是没有任何关系的
- 给大家一个思考题
看看布局文件的效果,可能你会诧异,为什么NestedScrollView下面会多一块出来?这里面卖一个关子,等我们说到自定义Behavior的时候再来解释
OK,大致的概念我们介绍完了,run看看。嗯,貌似跟一般线性布局也没啥特别的区别
初始效果
等下,好像有点不对劲啊,刚才我们说的滚动功能去哪了?原来还有一个属性 layout_scrollFlags。只要你对AppBarLayout中的子view加这个属性,并且至少包含scroll这个flag,那么他就能滚动起来。否则就像之前那样,一直固定在顶部没啥变化
app:layout_scrollFlags="scroll"
scroll
layout_scrollFlags
AppBarLayout中的子控件通过layout_scrollFlags这个属性或者setScrollFlags才能展现出他们的滚动行为。除了这个scroll以外,还有4个flag,他们分别是snap、enterAlways、enterAlwaysCollapsed、exitUntilCollapsed,下面我们分别来举例进行说明
scroll:所有想滚动的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。这个我们已经见识过了
snap:这个直接翻译成“折断”没有人能够明白。如果你曾经实现过自定义滑动View的话,这个效果应该很好理解:被"关心"的这个View如果显示了一半,就全显示出来,否则则隐藏。
app:layout_scrollFlags="scroll|snap"
snap
enterAlways:往上滑动的时候被"关心"的这个View隐藏,往下滑的时候显示
app:layout_scrollFlags="scroll|enterAlways"
enterAlways
enterAlwaysCollapsed:向上滑动的时候被"关心"的这个View隐藏,向下滑动时先展现一个最小高度,等到滑动到NestedScrollView最顶部的时候再完全展现出来
<android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_toolbar"
app:title="@string/app_name"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
android:layout_width="match_parent"
android:layout_height="200dip"
android:minHeight="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>
注意这边一定要有一个最小高度,即minHeight属性,并且enterAlwaysCollapsed一定要搭配enterAlways和scroll才能正常展现
enterAlwaysCollapsedexitUntilCollapsed:向上滚动的时候被"关心"的这个View逐渐折叠到最小高度并固定到顶端
<android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_toolbar"
app:title="@string/app_name"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:layout_width="match_parent"
android:layout_height="200dip"
android:minHeight="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>
注意这边也一定要有一个最小高度minHeight,并且exitUntilCollapsed一定要搭配scroll才能正常展现
这5种flag介绍完毕,最后提醒大家,如果你的AppBarLayout中包含其他的View,那么含有layout_scrollFlags的标签的View请布局在前面。因为AppBarLayout实际上是一个LinearLayout,可以想象一下顺序倒过来是一个什么样的场景,这里我就不多说了
CollapsingToolbarLayout
CollapsingToolbarLayout提供了一个可以折叠的Toolbar,它继承FrameLayout。给它设置layout_scrollFlags,它可以控制包含在其中的子控件(如:ImageView、Toolbar),去响应layout_behavior事件发出的相应scrollFlags滚动事件(移除屏幕或固定在屏幕顶端)
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/main_appbarlayout"
android:layout_width="match_parent"
android:layout_height="200dip">
<android.support.design.widget.CollapsingToolbarLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:expandedTitleMarginStart="64dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?attr/colorPrimaryDark"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:src="@mipmap/bg"
app:layout_collapseParallaxMultiplier="0.5"
app:layout_collapseMode="parallax"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.v7.widget.Toolbar
app:title="@string/app_name"
app:layout_collapseMode="pin"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
CollapsingToolbarLayout
这里一样简单说明下
- layout_collapseMode
有2种模式,
pin模式:即固定模式,在折叠的时候最后固定在顶端
parallax模式:即视差模式,在折叠的时候会有个视差折叠的效果
我们可以在布局中自己分别设置看看效果 - layout_collapseParallaxMultiplier
CollapsingToolbarLayout滑动时,子视图的视觉差。这个值的范围为0.0-1.0之间。为0的时候,你可以感觉到视图完全随NestedScrollView滚动;为1的时候,似乎又是完全不滚动 - contentScrim
这是ToolBar被折叠到顶部固定后的背景。 - expandedTitleMarginStart/expandedTitleMarginEnd
在ToolBar被折叠前文字部分的左右间距 - setExpandedTitleColor/setCollapsedTitleTextColor
设置还没收缩时状态下字体颜色与收缩后Toolbar上字体的颜色 - setTitle
使用CollapsingToolbarLayout时必须把title设置到CollapsingToolbarLayout上,设置到Toolbar上不会显示
到此为止,相比大家应该对CoordinatorLayout有了一定的了解,初步使用应该问题不大
参考文章
你肯定不晓得的CoordinatorLayout?
Android5.0+(CollapsingToolbarLayout)
网友评论