CoordinatorLayout 详解:布局协调与滚动效果实现
CoordinatorLayout 是在 Google I/O 2015 大会上发布的,遵循 Material Design 风格,包含在 support.design 包中。它结合 AppBarLayout、CollapsingToolbarLayout 等组件,可以产生各种炫酷的交互效果。本文将深入解析 CoordinatorLayout 的核心机制、常用组件及典型应用场景。
一、依赖配置
使用 CoordinatorLayout 前,需要在项目的 build.gradle 文件中添加相应的依赖。虽然旧版示例常使用 com.android.support:design,但在新项目中建议迁移至 AndroidX。
// 支持库版本(旧项目)
implementation 'com.android.support:design:23.3.0'
// AndroidX 版本(推荐新项目)
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
implementation 'com.google.android.material:material:1.2.0'
二、核心组件介绍
1. CoordinatorLayout
又名协调者布局,它是 support.design 包中的顶级父 View。简单来说,CoordinatorLayout 是一个超级强大的 FrameLayout,用来协调其子 View 并以触摸影响布局的形式产生动画效果。典型的子 View 包括 FloatingActionButton、SnackBar 等。
2. AppBarLayout
AppBarLayout 是 LinearLayout 的子类,通常用于放置 Toolbar 和 CollapsingToolbarLayout。必须在它的子 View 上设置 app:layout_scrollFlags 属性或者在代码中调用 setScrollFlags() 设置这个属性。
AppBarLayout 的子布局有 5 种滚动标识:
- scroll:所有想滚动出屏幕的 View 都需要设置这个 flag,没有设置的 View 将被固定在屏幕顶部。
- enterAlways:任意向下的滚动都会导致该 View 变为可见,启用快速'返回模式'。
- enterAlwaysCollapsed:假设定义了最小高度(minHeight)同时 enterAlways 也定义了,那么 View 将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。
- exitUntilCollapsed:当你定义了一个 minHeight,此布局将在滚动到达这个最小高度的时候折叠。
- snap:当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部 25% 显示,它将折叠;相反,如果它的底部 75% 可见,那么它将完全展开。
3. CollapsingToolbarLayout
CollapsingToolbarLayout 的作用是提供了一个可以折叠的 Toolbar,它继承自 FrameLayout。给它设置 layout_scrollFlags,它可以控制包含在 CollapsingToolbarLayout 中的控件(如 ImageView、Toolbar)在响应 layout_behavior 事件时作出相应的 scrollFlags 滚动事件(移除屏幕或固定在屏幕顶端)。
CollapsingToolbarLayout 可以通过 app:contentScrim 设置折叠时工具栏布局的颜色,通过 app:statusBarScrim 设置折叠时状态栏的颜色。默认 contentScrim 是 colorPrimary 的色值,statusBarScrim 是 colorPrimaryDark 的色值。
CollapsingToolbarLayout 的子布局有 3 种折叠模式(Toolbar 中设置的 app:layout_collapseMode):
- off:默认属性,布局将正常显示,无折叠行为。
- pin:CollapsingToolbarLayout 折叠后,此布局将固定在顶部。
- parallax:CollapsingToolbarLayout 折叠时,此布局也会有视差折叠效果。当 CollapsingToolbarLayout 的子布局设置了 parallax 模式时,还可以通过
app:layout_collapseParallaxMultiplier 设置视差滚动因子,值为 0~1。
4. NestedScrollView
在新版的 support-v4 兼容包里面有一个 NestedScrollView 控件,这个控件其实是 Material Design 中设计的一个控件,目的是跟 MD 中的其他控件兼容。应该说在 MD 中,RecyclerView 代替了 ListView,而 NestedScrollView 代替了 ScrollView,它们两个都可以用来跟 Toolbar 交互,实现上拉下滑中 Toolbar 的变化。在 NestedScrollView 的名字中其实就可以看出它的作用了,Nested 是嵌套的意思,而 Toolbar 基本需要嵌套使用。
5. FloatingActionButton
FloatingActionButton 就是一个漂亮的按钮,其本质是一个 ImageView。有一点要注意,Material Design 引入了 Z 轴的概念,就是所有的 View 都有了高度,它们一层一层贴在手机屏幕上,而 FloatingActionButton 的 Z 轴高度最高,它贴在所有 View 的最上面,没有 View 能覆盖它。
6. Behavior
Behavior 只有是 CoordinatorLayout 的直接子 View 才有意义。只要将 Behavior 绑定到 CoordinatorLayout 的直接子元素上,就能对触摸事件(touch events)、window insets、measurement、layout 以及嵌套滚动(nested scrolling)等动作进行拦截。Design Library 的大多功能都是借助 Behavior 的大量运用来实现的。当然,Behavior 无法独立完成工作,必须与实际调用的 CoordinatorLayout 子视图相绑定。具体有三种方式:通过代码绑定、在 XML 中绑定或者通过注释实现自动绑定。
三、常见特效实现
1. 浮动操作按钮效果
效果如下:

只要使用 CoordinatorLayout 作为基本布局,将自动产生向上移动的动画。浮动操作按钮有一个默认的 behavior 来检测 Snackbar 的添加并让按钮在 Snackbar 之上呈现上移与 Snackbar 等高的动画。
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:layout_anchor="@id/rvToDoList"
app:layout_anchorGravity="bottom|right|end"/>
</android.support.design.widget.CoordinatorLayout>
2. Toolbar 的扩展与收缩效果
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CoordinatorLayout>
3. 响应滚动事件
首先,需要让 Toolbar 包裹在 AppBarLayout 中。
然后,我们需要定义 AppBarLayout 与滚动视图之间的联系。在 RecyclerView 或者任意支持嵌套滚动的 view 比如 NestedScrollView 上添加 app:layout_behavior。support library 包含了一个特殊的字符串资源 @string/appbar_scrolling_view_behavior,它和 AppBarLayout.ScrollingViewBehavior 相匹配,用来通知 AppBarLayout 这个特殊的 view 何时发生了滚动事件,这个 behavior 需要设置在触发事件(滚动)的 view 之上。
当 CoordinatorLayout 发现 RecyclerView 中定义了这个属性,它会搜索自己所包含的其他 view,看看是否有 view 与这个 behavior 相关联。AppBarLayout.ScrollingViewBehavior 描述了 RecyclerView 与 AppBarLayout 之间的依赖关系。RecyclerView 的任意滚动事件都将触发 AppBarLayout 或者 AppBarLayout 里面 view 的改变。
AppBarLayout 里面定义的 view 只要设置了 app:layout_scrollFlags 属性,就可以在 RecyclerView 滚动事件发生的时候被触发。
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
app:layout_scrollFlags 属性里面必须至少启用 scroll 这个 flag,这样这个 view 才会滚动出屏幕,否则它将一直固定在顶部。可以使用的其他 flag 有:enterAlways、enterAlwaysCollapsed、exitUntilCollapsed。
4. 折叠效果
如果想制造 toolbar 的折叠效果,我们必须把 Toolbar 放在 CollapsingToolbarLayout 中。
通常,我们都是设置 Toolbar 的 title,而现在,我们需要把 title 设置在 CollapsingToolBarLayout 上,而不是 Toolbar。
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle("Title");
5. 视差效果
为了制造出这种效果,我们添加一个定义了 app:layout_collapseMode="parallax" 属性的 ImageView。
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"></android.support.v7.widget.Toolbar>
<ImageView
android:src="@drawable/cheese_1"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
android:minHeight="100dp"/>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_light"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/main.appbar"
android:layout_width="match_parent"
android:layout_height="300dp"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/main.collapsing"
android:layout_width="match_parent"
android:layout_height="250dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
=
=
=
=
=
=
=/>
四、常见问题与解决方案
1. NestedScrollView 冲突问题
在使用 CoordinatorLayout 时,经常遇到 NestedScrollView 内部滚动失效的问题。这通常是因为嵌套滚动链断裂导致的。确保最外层是 CoordinatorLayout,中间层是 AppBarLayout,内容层是 NestedScrollView 或 RecyclerView,并且内容层正确设置了 app:layout_behavior="@string/appbar_scrolling_view_behavior"。
2. fitsSystemWindows 设置不当
如果 fitsSystemWindows 设置不正确,可能导致状态栏遮挡内容或底部导航栏遮挡布局。建议在 CoordinatorLayout 及其直接子 View 上统一设置 android:fitsSystemWindows="true",并在 styles.xml 中配置相应的 padding 以适配系统栏。
3. 自定义 Behavior
如果需要更复杂的交互逻辑,可以继承 CoordinatorLayout.Behavior 并重写 onTouchEvent 或 onDependentViewChanged 方法。例如,可以让某个 View 跟随另一个 View 的滚动距离进行缩放。
五、总结
CoordinatorLayout 是 Android Material Design 中实现复杂滚动交互的核心组件。通过合理组合 AppBarLayout、CollapsingToolbarLayout 和 NestedScrollView,开发者可以轻松实现折叠标题栏、视差背景、悬浮按钮联动等高级效果。掌握 Behavior 机制和嵌套滚动原理,能够解决大部分布局冲突问题,提升用户体验。在实际开发中,建议优先使用 AndroidX 库,并注意处理不同屏幕尺寸下的适配问题。