AppbarLayout的简单用法

在许多App中看到, toolbar有收缩和扩展的效果, 例如:

AppbarLayout的简单用法

appbar.gif

要实现这样的效果, 需要用到:
CoordinatorLayout和AppbarLayout的配合, 以及实现了NestedScrollView的布局或控件.
AppbarLayout是一种支持响应滚动手势的app bar布局, CollapsingToolbarLayout则是专门用来实现子布局内不同元素响应滚动细节的布局.
与AppbarLayout组合的滚动布局(RecyclerView, NestedScrollView等),需要设置 app:layout_behavior = “@string/appbar_scrolling_view_behavior” .没有设置的话, AppbarLayout将不会响应滚动布局的滚动事件.
我们回到再前面一章"Toolbar的使用", 将布局改动如下:

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
xmlns:android=“http://schemas.android.com/apk/res/android
xmlns:app=“http://schemas.android.com/apk/res-auto
xmlns:tools=“http://schemas.android.com/tools
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=“com.truly.mytoolbar.MainActivity”>
<android.support.design.widget.AppBarLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width=“match_parent”
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags=“scroll”
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title=“Title” />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width=“match_parent”
android:layout_height=“match_parent”
app:layout_behavior="@string/appbar_scrolling_view_behavior">

</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

先看下效果再来解释为什么.

appbar_2.gif

可以看到,

随着文本往上滚动, 顶部的toolbar也往上滚动, 直到消失.
随着文本往下滚动, 一直滚到文本的第一行露出来, toolbar也逐渐露出来

解释:
从上面的布局中可以看到, 其实在整个父布局CoordinatorLayout下面, 是有2个子布局

AppbarLayout
NestedScrollView
NestedScrollView先放一放, 我们来看AppbarLayout.

AppBarLayout 继承自LinearLayout,布局方向为垂直方向。所以你可以把它当成垂直布局的LinearLayout来使用。AppBarLayout是在LinearLayou上加了一些材料设计的概念,它可以让你定制当某个可滚动View的滚动手势发生变化时,其内部的子View实现何种动作。

注意:
上面提到的"某个可滚动View", 可以理解为某个ScrollView. 就是说,当某个ScrollView发生滚动时,你可以定制你的“顶部栏”应该执行哪些动作(如跟着一起滚动、保持不动等等)。
这里某个ScrollView就是NestedScrollView或者实现了NestedScrollView机制的其它控件, 如RecyclerView. 它有一个布局行为Layout_Behavior:
app:layout_behavior="@string/appbar_scrolling_view_behavior"

这是一个系统behavior, 从字面意思就可以看到, 是为appbar设置滚动动作的一个behavior. 没有这个属性的话, Appbar就是死的, 有了它就有了灵魂.
我们可以通过给Appbar下的子View添加app:layout_scrollFlags来设置各子View执行的动作. scrollFlags可以设置的动作如下:

(1) scroll: 值设为scroll的View会跟随滚动事件一起发生移动。就是当指定的ScrollView发生滚动时,该View也跟随一起滚动,就好像这个View也是属于这个ScrollView一样。

上面这个效果就是设置了scroll之后的.

(2) enterAlways: 值设为enterAlways的View,当任何时候ScrollView往下滚动时,该View会直接往下滚动。而不用考虑ScrollView是否在滚动到最顶部还是哪里.

我们把layout_scrollFlags改动如下:
app:layout_scrollFlags=“scroll|enterAlways”

效果如下:

appbar_3.gif

(3) exitUntilCollapsed:值设为exitUntilCollapsed的View,当这个View要往上逐渐“消逝”时,会一直往上滑动,直到剩下的的高度达到它的最小高度后,再响应ScrollView的内部滑动事件。

怎么理解呢?简单解释:在ScrollView往上滑动时,首先是View把滑动事件“夺走”,由View去执行滑动,直到滑动最小高度后,把这个滑动事件“还”回去,让ScrollView内部去上滑。
把属性改下再看效果
<android.support.v7.widget.Toolbar

android:layout_height="?attr/actionBarSize"
android:minHeight=“20dp”
app:layout_scrollFlags=“scroll|exitUntilCollapsed”
/>

appbar_4.gif

(4) enterAlwaysCollapsed:是enterAlways的附加选项,一般跟enterAlways一起使用,它是指,View在往下“出现”的时候,首先是enterAlways效果,当View的高度达到最小高度时,View就暂时不去往下滚动,直到ScrollView滑动到顶部不再滑动时,View再继续往下滑动,直到滑到View的顶部结束

这个得把高度加大点才好实验. 来看:
<android.support.v7.widget.Toolbar

android:layout_height=“200dp”
android:minHeight="?attr/actionBarSize"
app:layout_scrollFlags=“scroll|enterAlways|enterAlwaysCollapsed”
</android.support.design.widget.AppBarLayout>

appbar_5.gif

Attention:
其实toolbar的默认最小高度minHeight就是"?attr/actionBarSize" , 很多时候可以不用设置. 而且从图上可以看出, 其实这里有个缺陷, 就是title的位置和toolbar上的图标行脱离了, 即使在布局里添加了 android:gravity=“bottom|start”, 在toolbar滚动的时候, title还在, 图标滚动到隐藏了.

image.png

后面讲解的CollapsingToolbarLayout可以解决这个问题, 这里先丢出来.

(5) snap:简单理解,就是Child View滚动比例的一个吸附效果。也就是说,Child View不会存在局部显示的情况,滚动Child View的部分高度,当我们松开手指时,Child View要么向上全部滚出屏幕,要么向下全部滚进屏幕,有点类似ViewPager的左右滑动

appbar_6.gif

引入CollapsingToolbarLayout
CollapsingToolbarLayout是用来对Toolbar进行再次包装的ViewGroup,主要是用于实现折叠(其实就是看起来像伸缩~)的App Bar效果。它需要放在AppBarLayout布局里面,并且作为AppBarLayout的直接子View。CollapsingToolbarLayout主要包括几个功能(参照了官方网站上内容,略加自己的理解进行解释):

(1) 折叠Title(Collapsing title):当布局内容全部显示出来时,title是最大的,但是随着View逐步移出屏幕顶部,title变得越来越小。你可以通过调用setTitle方法来设置title。

(2)内容纱布(Content scrim):根据滚动的位置是否到达一个阀值,来决定是否对View“盖上纱布”。可以通过setContentScrim(Drawable)来设置纱布的图片. 默认contentScrim是colorPrimary的色值

(3)状态栏纱布(Status bar scrim):根据滚动位置是否到达一个阀值决定是否对状态栏“盖上纱布”,你可以通过setStatusBarScrim(Drawable)来设置纱布图片,但是只能在LOLLIPOP设备上面有作用。默认statusBarScrim是colorPrimaryDark的色值.

(4)视差滚动子View(Parallax scrolling children): 子View可以选择在当前的布局当时是否以“视差”的方式来跟随滚动。(PS:其实就是让这个View的滚动的速度比其他正常滚动的View速度稍微慢一点)。将布局参数app:layout_collapseMode设为parallax

(5)将子View位置固定(Pinned position children):子View可以选择是否在全局空间上固定位置,这对于Toolbar来说非常有用,因为当布局在移动时,可以将Toolbar固定位置而不受移动的影响。 将app:layout_collapseMode设为pin。

我们来更改一下布局:

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
…>

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="150dp">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_collapseMode="parallax"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:title="Title" />
    </android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="16dp"
        android:lineSpacingMultiplier="2"
        android:text="@string/textContent" />
</android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

可以看到, 我们把原本属于toolbar的几个属性移到了CollapsingToolbarLayout上. 分别是:
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags=“scroll|exitUntilCollapsed”

同时给toolbar增加了一个折叠模式属性
app:layout_collapseMode=“parallax”

我们来看下效果:

appbar_8.gif

嗯嗯, 折叠模式不对, toolbar的顶部图标没了. 我们改下折叠模式:
app:layout_collapseMode=“pin”

再看效果:

appbar_9.gif

我们把scrollFlags属性改下, 看下对比:
app:layout_scrollFlags=“scroll|enterAlways|enterAlwaysCollapsed”

appbar_10.gif

效果还是蛮不错的, 有了点Google Material Design的感觉了.
上面说CollapsingToolbarLayout是个ViewGroup, 那么肯定还可以添加控件. 那么我们在里面添加一个ImageView来看看. 更改布局如下:

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
…>
<android.support.design.widget.AppBarLayout
android:layout_width=“match_parent”
android:layout_height=“200dp”>
<android.support.design.widget.CollapsingToolbarLayout

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags=“scroll|enterAlways|enterAlwaysCollapsed”>

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width=“match_parent”
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_collapseMode=“pin”
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title=“Title” />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
    ...
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <TextView
        ... />
</android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

来看下效果:

appbar_11.gif

嗯, 有了点意思, 但不美观, 上部的toolbar和图片不协调. toolbar应该有默认的背景属性, 我们去掉它看看.
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width=“match_parent”
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode=“pin”
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title=“Title” />

再看下效果:

appbar_12.gif

这次真的不错哦, 已经和很多大公司的app相像了. 但是为什么去掉toolbar的background就可以得到透明背景呢? 说句实话, 没找到原因.
不过我们没有给CollapsingToolbarLayout设置contentScrim属性哦, 给它加个属性看看.
<android.support.design.widget.CollapsingToolbarLayout

app:contentScrim="?attr/colorPrimary"
…>

appbar_13.gif

嗯嗯, 好像还不如没设置这个属性好呢.
什么时候需要contentScrim属性呢?
因为这个布局里面给CollapsingToolbarLayout的layout_scrollFlags设置的是 “scroll|enterAlways|enterAlwaysCollapsed” , toolbar会全部消失的, 所以感觉不是很美观. 如果将layout_scrollFlags属性改为 “scroll|exitUntilCollapsed” , 效果会好点, 适合toolbar还是需要展示的场合.

appbar_14.gif

不管怎么样, 先去掉contentScrim属性吧.
目前有很多APP比较喜欢采用沉浸式设计, 简单点说就是将状态栏和导航栏都设置成透明或半透明的.
我们来把状态栏statusBar设置成透明. 在style主题中的AppTheme里增加一条:

在布局里面, 将ImageView和所有它上面的父View都添加fitsSystemWindows属性.

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout

android:fitsSystemWindows=“true”>
<android.support.design.widget.AppBarLayout

android:fitsSystemWindows=“true”>
<android.support.design.widget.CollapsingToolbarLayout

android:fitsSystemWindows=“true”>
<ImageView

android:fitsSystemWindows=“true” />
<android.support.v7.widget.Toolbar
… />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
    ...>
    <TextView
        ... />
</android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

最后来看下效果:

appbar_15.gif

其实还可以在CollapsingToolbarLayout里设置statusBarScrim为透明色, 不过有点问题, 最顶部的toolbar没有完全隐藏, 还留了一点尾巴.

image.png

难道就这个属性就没用吗? 我们把layout_scrollFlags改成 “scroll|exitUntilCollapsed” 看看:

image.png

这个时候toolbar不用隐藏, 所以还是美美的.
AppbarLayout整个做成沉浸式之后, 状态栏的图标可能会受到封面图片颜色过浅的影响, 可以给其加一个渐变的不透明层.
渐变遮罩设置方法:
在res/drawable文件夹下新建一个名为status_gradient的xml资源文件, 代码如下:

<?xml version="1.0" encoding="utf-8"?>

布局中, 在ImageView下面增加一个View, 背景设为上面的渐变遮罩.

给遮罩设置折叠模式: app:layout_collapseMode=“pin” , 折叠到顶部后定住. 来看下效果.

image.png

image.png

上图是展开状态的对比, 后面的是没有添加遮罩的效果, 前面是添加了遮罩的效果. 下图是添加了遮罩折叠后的效果. 有点黑暗系影片的感觉哦.
FloatingActionButton再次表演
作为Google Material Design的一个重要控件, FloatingActionButton怎么可能不在AppbarLayout中起点作用呢. 我们在布局中加一个悬浮按钮, 让它的锚点挂载Appbar的右下角. 这样这个悬浮按钮就和Appbar关联起来了.
<android.support.design.widget.CoordinatorLayout
…>

<android.support.design.widget.AppBarLayout
    ...
</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView
...
</android.support.v4.widget.NestedScrollView>

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:src="@drawable/ic_share_white_24dp"
    android:elevation="4dp"
    app:pressedTranslationZ="16dp"
    app:rippleColor="@android:color/white"
    app:layout_anchor="@id/appbar"
    app:layout_anchorGravity="bottom|end"/>

</android.support.design.widget.CoordinatorLayout>

我们来看下效果.

appbar_16.gif

好吧, 美美的Toolbar完成了, 有点Google Material Design扑面而来的感觉了.

作者:朋朋彭哥
链接:https://www.jianshu.com/p/bbc703a0015e