CoordinatorLayout等控件实现Google Material Design效果

本文介绍以下很 google的UI控件:CoordinatorLayout、AppbarLayout、NestedScrollView以及CollapsingToolbarLayout。它们共同实现了下面这样的效果:

  1. 初始控件

「CoordinatorLayout」:本质是一个「FrameLayout」,是一个“super-powered FrameLayout”,它的主要作用:作为顶层布局,协调子View之间的交互;

「AppBarLayout」:是一种支持响应滚动手势的app bar布局,与「CoordinatorLayout」控件一起使用,实现「AppBarLayout」内部子View的Material Design滚动效果;

「NestedScrollView」:若是需要「AppbarLayout」中的子View实现滚动效果,需要配合实现一个带有「Scroll」属性的View,这个View最好是「NestedScrollView」、「RecyclerView」或是存在「Scroll」属性的控件模块(本文是一个带有RecyclerView的Fragment模块);

「CollapsingToolbarLayout」:「CollapsingToolbarLayout」是一个ViewGroup,看名字知道,它主要是用来包装「Toolbar」控件,实现折叠(其实就是看起来像伸缩~)的效果,它需要作为「AppBarLayout」布局的直接子View。

  1. layout_scrollFlags和layout_behavior

使用「CoordinatorLayout」、「AppBarLayout」以及实现了「Scroll」属性的「Fragment」共同实现Toolbar的滚动显示和隐藏效果。 先上效果图以及代码:
CoordinatorLayout等控件实现Google Material Design效果_第1张图片

main_activity.layout




    

        
    

    

fragment.layout



    
    

        

        
    

代码中用到的依赖库:

    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation "com.android.support:design:$support_version"
    implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.0.3'

上面的代码简单实现了Toolbar的滚动显示和滚动隐藏效果。在这里需要了解实现这样效果的两个关键属性:「app:layout_scrollFlags」、「app:layout_behavior」。

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

「app:layout_behavior」:控件「ConstraintLayout」中实现「NestedScrollView」机制(Fragment模块中包含RecyclerView控件)的子View(不包括AppBarLayout),设置该属性的固定value:

app:layout_behavior="@string/appbar_scrolling_view_behavior"

这是一个系统behavior, 从字面意思就可以看到, 是为appbar设置滚动动作的一个behavior. 没有这个属性的话, Appbar就是死的, 有了它就有了灵魂.

我们可以通过给Appbar下的子View添加app:layout_scrollFlags来设置各子View执行的动作. scrollFlags可以设置的动作如下:
「app:layout_scrollFlags」

  • scroll: 值设为scroll的View(Toolbar)会跟随滚动事件一起发生移动。即当带有「Scroll」属性的View滚动时,该View(Toolbar)也跟随一起滚动,就好像这个View也是属于这个ScrollView一样。
    上面的效果图就是Toolbar设置了app:layout_scrollFlags="scroll"的效果图。

  • enterAlways: 顾名思义,View(Toolbar)总是(Always)“进入”。这个“进入”的意思,包括显示和隐藏;也即任何时候ScrollView滚动时,该View(Toolbar)都会同步滚动出现(隐藏)。

修改Toolbar的app:layout_scrollFlags:

app:layout_scrollFlags="scroll|enterAlways"

  • exitUntilCollapsed:直译过来是“折叠之后退出”。当此View(Toolbar)要往上逐渐“隐藏”时,会一直往上滑动,直到折叠完成(剩下「最小高度」)后,再响应ScrollView的内部滑动事件。
    修改代码,直接查看效果图可能更容易理解:
        

  • enterAlwaysCollapsed:看名字,结合了enterAlways与Collapsed(折叠)功能。当View(Toolbar)滚动“出现”时,首先是enterAlways效果,当View(Toolbar)显示出来的部分达到最小高度时,View就暂时停止滚动;ScrollView继续滑动到顶部时,View(Toolbar)再继续往下滑动,直到滑到View(Toolbar)的顶部结束。

这个效果,需要设置View(Toolbar)的最大和最小高度(Toolbar默认的最小高度就是?attr/actionBarSize),直接上代码和效果图,理解这个效果:

        

注意:从图上可以看出, 在最大高度时,title的位置和toolbar上的图标行脱离了;并且View(Toolbar)滚动出现到最小高度时,title和图标都没有出现。 即使在布局里添加了 android:gravity=“bottom|start”, 在toolbar滚动的时候, title还在, 图标依然滚动到隐藏了,这个问题在后边的「CollapsingToolbarLayout」控件中得以解决。

  • snap:View(Toolbar)带有“吸附”效果。即View(Toolbar)不会存在局部显示的情况:要不完全隐藏,要不完全显示,有点类似ViewPager的左右滑动。
    上代码和效果图:
        

CoordinatorLayout等控件实现Google Material Design效果_第2张图片

  1. 「CollapsingToolbarLayout」控件
    直译名称:“折叠Toolbar的布局”,根据名称理解控件的功能。理解的意思是对Toolbar进行包装的ViewGroup,实现Toolbar的折叠(其实就是看起来像伸缩~)的效果。它需要放在AppBarLayout布局里面,并且作为AppBarLayout的直接子View。主要的功能介绍如下:
  • (1) 折叠Title(Collapsing title):布局完全显示时,Toolbar的Title是最大的;但随着View(Toolbar)逐步折叠滚动隐藏,title逐渐减小并最终显示在Toolbar正常大小情况下的Title位置。

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

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

  • (4)视差滚动子View(Parallax scrolling children): View(CollapsingToolbarLayout中可以有多个子View,此View不限于Toolbar)在当前的布局中按照“视差”的方式来跟随滚动。(PS:其实就是让这个View的滚动的速度比其他正常滚动的View速度稍微慢一点)。将布局参数app:layout_collapseMode设为parallax

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

按照上面对于控件「CollapsingToolbarLayout」的功能描述,我们对代码中「AppBarLayout」控件及其子View进行修改,并查看效果图:



        

            

        
    

可以看到,我们使用「CollapsingToolbarLayout」对「Toolbar」控件进行包装,并将一些原属于「AppBarLayout」布局内部的属性提取到「CollapsingToolbarLayout」控件中:

            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:popupTheme="@style/AppTheme.PopupOverlay"

同时,设置「CollapsingToolbarLayout」的“内容纱布”的代码为:

app:contentScrim="?attr/colorPrimary"

设置「Toolbar」的位置固定:

app:layout_collapseMode="pin"

效果如图:

基本达到了文章开头的效果图样式,现在还欠缺的就是「Toolbar」最大化显示时的背景图片、背景图片的折叠效果、内容(Toolbar)栏以及状态栏的UI变化效果。

上面既然介绍到控件「CollapsingToolbarLayout」是一个ViewGroup,那么它就可以再容纳其他的子View,此时就可以添加我们需要的背景图片了。

看代码:



            
                
            

        

代码中在控件「CollapsingToolbarLayout」的布局中,增加一个ImageView类型的子View,并添加如下「CollapsingToolbarLayout」的功能代码

app:layout_collapseMode="parallax"

再看效果图:

注意:代码中设置的没有设置「Toolbar」的背景,只是在「CollapsingToolbarLayout」布局中增加了“内容纱布”背景代码。所以在「Toolbar」完全显示时的背景栏为透明的,达到最小高度时的背景为「ColorPrimary」

这节最后再说一下,因为目前大部分的app设计都是沉浸式的状态栏,即状态栏透明,与「Toolbar」的背景一样。所以我们可以将状态栏的背景设置为透明,这就需要修改主题了:

    

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




    

        

            
     ...

效果图:
CoordinatorLayout等控件实现Google Material Design效果_第3张图片

  1. 「FloatingActionButton」
    作为Google Material Design的一个重要控件,「FloatingActionButton」怎么可能不在「AppBarLayout」中起点作用。就像文章开头那里的效果一样, 我们在布局中加一个悬浮按钮,并让它“锚定”在「AppBarLayout」控件的右下角. 这样这个悬浮按钮就和「AppBarLayout」关联起来了。

还是看代码,看效果图:




    

        

            

            

        
    

    

    

效果图:

最后的最后,文章开头的效果图中,还有一个「TabLayout」控件的效果。这个就直接在「Fragment」对应的布局文件中增加控件,并在代码中添加对应的「TabLayout.Tab」就可以了。

上代码:
fragment.layout




    

    

        

        
    

java代码中增加「TabLayout.Tab」:

...
val tabLayout = view.findViewById(R.id.tabs)

        tabLayout.addTab(tabLayout.newTab().setText("标题一"))
        tabLayout.addTab(tabLayout.newTab().setText("标题二"))
        tabLayout.addTab(tabLayout.newTab().setText("标题三"))
        tabLayout.addTab(tabLayout.newTab().setText("标题四"))
        val defaultTab = tabLayout.newTab().setText("标题五")
        tabLayout.addTab(defaultTab)
        defaultTab.select()
        tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab) {
                var pos = tab.position
            }

            override fun onTabUnselected(tab: TabLayout.Tab) {
                var pos = tab.position
            }

            override fun onTabReselected(tab: TabLayout.Tab) {
                var pos = tab.position
            }
        })
 ...

最终的效果图,就是文章开头的样子了。

OK,到这里终于搞定这个比较Material Design的Toolbar效果了。本文参考了如下文章:

https://www.jianshu.com/p/bbc703a0015e

你可能感兴趣的:(Android-Toolbar)