一、概述

Android 的历史进程中,大概有 TitleBar、ActionBar、Toolbar 的进化,这是 Android 设计语言的改良过程。而后来随着 Material Design 设计的出现,它又提供了 AppBar 的概念,而 AppBarLayout 则是 AppBar 在 Android 中的代码实现。

对于Toolbar不了解的同学,请参照我的上一篇博客


引用下别人的图:

android ble 搜索没有名字_android ble 搜索没有名字

1.1 什么是 Material Design

谷歌于2014年的 Google I/O 大会上首次亮相推出了全新的 设计语言 Material Design。谷歌表示,这种设计语言旨在为手机、平板电脑、台式机和"其他平台"提供更一致、更广泛的"外观和感觉"。

1.2 关于Material Design

Material Design的出现使得 Android 也能定制高颜值的界面,并且指导了如何实现复杂炫丽的交互效果,而 Android Surpport Desgin 这个支持包就是 Android 官方对 Material Design 的代码实现。

Android Support Desgin 这个包中提供了一系列的组件如:CoordinatorLayout、AppBarLayout、FloatingActionButton 等等。其中 CoordinatorLayout 是核心,它是包内其它组件能够正常工作的前提。

本文的目的就是详细地介绍怎么样通过 AppBarLayout 与 CoordinatorLayout 的配合使用,去定制一个可折叠的 Toolbar。然后继续通过 CollapsingToolbarLayout 进一步增强 Toolbar 的视觉效果。

先看下效果,有兴趣再慢慢学习!

高流量预警,本篇博客插入了大量的图片,建议WIFI下打开!

android ble 搜索没有名字_sed_02

二、什么是CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout ?

官方给出的使用示例

<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.support.v4.widget.NestedScrollView
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             app:layout_behavior="@string/appbar_scrolling_view_behavior">

         <!-- Your scrolling content -->

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

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

         <android.support.v7.widget.Toolbar
                 ...
                 app:layout_scrollFlags="scroll|enterAlways"/>

         <android.support.design.widget.TabLayout
                 ...
                 app:layout_scrollFlags="scroll|enterAlways"/>

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

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

android ble 搜索没有名字_android ble 搜索没有名字_03

CoordinatorLayout

CoordinatorLayout是一个“加强版”的 FrameLayout,它主要有两个用途:
(1) 用作应用的顶层布局管理器;
(2) 通过为子View指定 behavior 实现自定义的交互行为。
重点:在我们做 Material Design 风格的app时通常都使用 CoordinatorLayout 作为布局的根节点,以便实现特定的UI交互行为。

AppBarLayout

(1)AppBarLayout 继承自LinearLayout,子控件默认为竖直方向显示,可以用它实现Material Design 的Toolbar;
(2)它支持滑动手势;它的子控件可以通过在代码里调用setScrollFlags(int)或者在XML里app:layout_scrollFlags来设置它的滑动手势。这里的滑动手势可以理解为:当某个可滚动View(如NestedScrollView)的滚动手势发生变化时,AppBarLayout内部的子View(如Toolbar)实现某种动作。
注意:
①实现这些的前提是它的根布局必须是 CoordinatorLayout。
②AppBarLayout的子控件不仅仅可以设置为Toolbar,也可以包含其他的View。

CollapsingToolbarLayout

CollapsingToolbarLayout作用是提供了一个可以折叠的Toolbar,它继承自FrameLayout,给它设置layout_scrollFlags,它可以控制包含在CollapsingToolbarLayout中的控件(如:ImageView、Toolbar)在响应layout_behavior事件时作出相应的scrollFlags滚动事件(移除屏幕或固定在屏幕顶端等)。
注意:CollapsingToolbarLayout继承自FrameLayout!
CollapsingToolbarLayout继承自FrameLayout!
CollapsingToolbarLayout继承自FrameLayout!

在 Android 为实现 Material Design 提供的支持包 android support design 中,CoordinatorLayout 毫无疑问是最核心的,它通过子 View 对象配置的 Behavior,实现了子 View 与 CoordinatorLayout、子 View 与子 View 之间一系列复杂的交互。

所以CoordinatorLayout作为一个顶层布局管理器存在。

AppBarLayout 需要和一个独立的兄弟 View 配合使用,这个兄弟 View 是一个嵌套滑动组件,只有这样 AppBarLayout 才能知道什么时候开始滑动。它们之间关系的绑定通过给嵌套滑动的组件设立特定的 Behavior,那就是 AppBarLayout.ScrollingViewBehavior。
NestedScrollView 就是那个配套的滑动组件,它需要和 AppBarLayout 进行绑定,所以它必须指定 Behavior。在 xml 中通过

app:layout_behavior="@string/appbar_scrolling_view_behavior"

嵌套滑动的组件一定要是 NestedScrollView 吗?

当然不是,在 CoordinatorLayout 中嵌套滑动的本质是一个 NestedScrollingChild 对象。 NestedScrollingChild 是一个接口,目前它的实现类有 4 个。
除了使用 NestedScrollView,我们还经常使用 RecyclerView 和 SwipeRefreshLayout 作为配套的嵌套滑动组件。

android ble 搜索没有名字_android_04

懂的了这些之后,通过给AppBarLayout添加layout_scrollFlags就可以实现滑动效果啦!
下面将详细讲述什么是layout_scrollFlags:
先上代码

<?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=".ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        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="?actionBarSize"
                app:title="Toolbar"/>
    </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:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/text_margin"
            android:text="@string/large_text" />

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

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

layout_scrollFlags 取值有 5 个,上面说过,AppBarLayout的子控件可以通过在代码里调用setScrollFlags(int)或者在XML里app:layout_scrollFlags来设置它的滑动手势。可以看出,这里我们的子控件就是Toolbar。

1.scroll
2.enterAlways
3.enterAlwaysCollapsed
4.exitUntilCollapsed
5.snap

scroll(滑动)

android ble 搜索没有名字_android ble 搜索没有名字_05


scroll(滑动)是基础,后面的几个属性都是建立在此属性上的。只有在 AppBarLayout 中的子 View 配置了 scroll 属性,这个 AppBarLayout 才会响应。

运行:app:layout_scrollFlags=“scroll”
结果:Toolbar和NestedScrollView 同步响应滑动,Toolbar像是NestedScrollView的一部分。

enterAlways、enterAlwaysCollapsed、exitUntilCollapsed

enterAlways注意看文字的变化

android ble 搜索没有名字_android ble 搜索没有名字_06

运行:app:layout_scrollFlags=“scroll|enterAlways”
结果:该方法向下滑动时,Toolbar先滑动进入视野,直到Toolbar全部显示后才显示NestedScrollView。

enterAlwaysCollapsed

修改下layout_height=“250dp”,minHeight=“100dp”(只是为了演示效果做的修改)

android ble 搜索没有名字_android_07

运行:app:layout_scrollFlags=“scroll|enterAlways|enterAlwaysCollapsed”
结果:该方法向下滑动时,Toolbar先滑动进入视野,因为多了Collapsed(折叠),限制了Toolbar的行为。也就是Toolbar不会完全显示出来才滑动NestedScrollView,而是会先显示一部分。默认先显示一个Toolbar的高度。我们可以通过android:minHeight指定默认显示的高度(这里我们已经设置了minHeight=“100dp”)。当默认高度(100dp)显示出来后,再滑动NestedScrollView,当NestedScrollView显示完成后,继续显示Toolbar剩余部分。
也就是向下滑动分3步进行:
1)先滑动指定的Toolbar的高度,通过minHeight设置
2)滑动NestedScrollView直至顶部
3)滑动剩余的Toolbar的高度

exitUntilCollapsed

android ble 搜索没有名字_android_08

运行:app:layout_scrollFlags=“scroll|exitUntilCollapsed”
结果:和上面的enterAlwaysCollapsed类似(先向下滑动到指定高度再滑动NestedScrollView再滑动剩余部分),exitUntilCollapsed是先向上滑动到指定高度再滑动NestedScrollView,区别是剩余部分(minHeight)不滑动。

可以看出来,scroll 和 exitUntilCollapsed 其实没有什么太大的区别。
区别在于 exitUntilCollapsed 的存在,让 scroll 滑动受到了一定的限制。这个限制就是 scoll 不再能够进行完全的滑动,因为 collapsed 距离的存在。

我这里总结下自己对enter和exit的理解(可能不正确,只是方便理解):把Toolbar当成操作对象
enter:【Toolbar】进入【视野】。向下滑动(只对向下滑动各个组件是怎么显示的做限制,向上滑动时就像一个整体同步响应滑动)
exit:【Toolbar】离开【视野】。向上滑动(只对向上滑动各个组件是怎么显示的做限制,向下滑动时就像一个整体同步响应滑动)

①当使用enter时,即进入,Toolbar先进入视野,然后再是NestedScrollView。对离开没有进行操作,故和scroll一样。
②当使用exit时,即离开。Toolbar先离开视野,然后再是NestedScrollView滑动。对进入没有进行操作,故和scroll一样。

snap

android ble 搜索没有名字_android_09

表示一种吸附效果。说白点就是如果向下滑动不到Toolbar的一半,Toolbar就不会完全显示,向上滑动不到Toolbar的一半,Toolbar就不会完全收缩。

按道理说,因为有 AppBarLayout 的存在,Toolbar 已经多姿多彩了,我们可以利用它们的特性实现很漂亮的 Appbar。
但是,人们仍然不满足,人们希望 Toolbar 的效果能够更加炫丽一些,于是 CollapsingToolbarLayout 这个类出现了。

CollapsingToolbarLayout

刚刚说过,CollapsingToolbarLayout 出现的目的只是为了增强 Toolbar。

它为 Toolbar 带来了下面几个特性。

1.Collapsing Title 可折叠的标题
2.Content Scrim 内容纱布
3.Statusbar Scrim 状态栏纱布
4.Parallax scrolling children 子 View 的视差滚动行为
5.Pinned position children 子类的位置固定行为

代码体现如下:

android ble 搜索没有名字_Android_10


经过测试:这里设置minHeight无效,那Collapsed怎么发挥效果呢?内部已经规定好了吧。不懂…

分别解释下:

Collapsing Title(可折叠的标题)

注意:这里的title是CollapsingToolbarLayout的title,如果Toolbar也有title,显示的是CollapsingToolbarLayout的。两者都会位置和大小会随着状态改变。

Content Scrim(内容纱布)

内容纱布其实就是为了更好地反馈CollapsingToolbarLayout 中折叠状态的变化。可以将这个变化指定为某种颜色的改变或者图片,它就像是一块纱布一样遮住 title 下面的内容,所以被称为内容纱布。
注意:我说的是 Content scrim 会遮住 title 下方的内容部分。如果一个 CollapsingToolbarLayout 中只有 Toolbar 的话,那么它就不起作用。CollapsingToolbarLayout 本质上是一个 FrameLayout,所以需要在 Toolbar 的前面位置加入其它的 View 作为内容,Content scrim 才会起作用。

Statusbar Scrim(状态栏纱布)

和Content Scrim类似,不过这次作用的对象是statusBar。
注意!注意!注意!
修改状态栏状态(statusBarColor)是在Android 5.0之后才有的特性,也就是最低为API21。设置一下就可以了,因为现在基本创建Android基本都是Android 6.0以上了。

①AppBarLayout要设置android:fitsSystemWindows=“true”
②statusBarColor的颜色要是设置为全透明(transparent)。

<item name="android:statusBarColor">@android:color/transparent</item>

或者半透明(没测试过)

<item name="android:windowTranslucentStatus">true</item>
Parallax scrolling children 子 View 的视差滚动行为

CollapsingToolbarLayout 可以控制的子 View 滚动模式有 3 种:
1.none------默认,无任何效果
2.Parallax------视差滚动
3.pin------固定某个 View
它通过 xml 属性 app:layout_collapseMode 来设置。需要注意的是,这个属性作用对象是 CollapsingToolbarLayout 中的子 View 并不是 CollapsingToolbarLayout。

如何理解视差?

就是滚动的速度不同,造成的视觉差异效果。也就是说 CollapsingToolbarLayout 中有的 view 滚动的快一些,其它的滚动的慢一些。
它滚动的快慢受 Parallax multiplier 这个因子的影响,默认值为DEFAULT_PARALLAX_MULTIPLIER。也就是 0.5f。也就是正常速度的一半。

Pinned position children 子类的位置固定行为

将 CollapsingToolbarLayout 中某个子 View 固定,无论是否存在滚动事件,只要设置 app:layout_collapseMode=”pin”,视图都不会移动。

回头看,为什么要设置fitsSystemWindows="true"呢?

因为fitsSystemWindows属性可以让view根据系统窗口来调整自己的布局;简单点说就是我们在设置应用布局时是否考虑系统窗口布局,这里系统窗口包括系统状态栏、导航栏、输入法等,包括一些手机系统带有的底部虚拟按键。下面给出图片直观点
当设置了透明状态栏(StatusBar)和透明导航栏(NavigationBar)时,引用下别人的图

android ble 搜索没有名字_sed_11


可以看到,我们的状态栏(StatusBar)或导航栏(NavigationBar)就会变成透明,并且布局会扩展到StatusBar或NavigationBar的位置。


当设置了fitSystemWindows=“true”之后


android ble 搜索没有名字_sed_12

如果想监听 AppBarLayout 中的滑动位移信息,那么添加相应的监听器就好了。

OnOffsetChangedListener

这是 AppBarLayout 定义的监听器。

android ble 搜索没有名字_sed_13

verticalOffset 是 AppBarLayout 相对于完全展开时没有滑动的距离。它在初始位置为 0,其它时候都为负数。它绝对值的最大值为 AppBarLayout 的 TotalScollRange。
明白了这个之后,就可以根据 verticalOffset 做透明度动画等丰富的效果了。