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的滚动显示和隐藏效果。 先上效果图以及代码:
在这里插入图片描述

main_activity.layout

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

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>
    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

fragment.layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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=".fragment.mvp.view.imp.MainView">
    
    <com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:id="@+id/record_list_refresh_layout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabs">

        <com.scwang.smartrefresh.layout.header.ClassicsHeader
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:srlAccentColor="@color/white"
            app:srlPrimaryColor="@color/red_pressed"/>

        <com.ecg.rencarehealth.recordcollect.view.SwapMenuRecyclerView.SwapRecyclerView
            android:id="@+id/record_list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"/>
    </com.scwang.smartrefresh.layout.SmartRefreshLayout>
</android.support.constraint.ConstraintLayout>

代码中用到的依赖库:

    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的内部滑动事件。
    修改代码,直接查看效果图可能更容易理解:
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:minHeight="16dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

在这里插入图片描述

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

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

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="188dp"
            android:minHeight="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

在这里插入图片描述

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

  • snap:View(Toolbar)带有“吸附”效果。即View(Toolbar)不会存在局部显示的情况:要不完全隐藏,要不完全显示,有点类似ViewPager的左右滑动。
    上代码和效果图:
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            app:layout_scrollFlags="scroll|snap"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

在这里插入图片描述

  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进行修改,并查看效果图:

<android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_210"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:contentScrim="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                app:layout_collapseMode="pin"/>

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

可以看到,我们使用「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,此时就可以添加我们需要的背景图片了。

看代码:

<android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:contentScrim="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@drawable/test_image_view"
                app:layout_collapseMode="parallax"/>
                
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                app:layout_collapseMode="pin"/>

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

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

app:layout_collapseMode="parallax"

再看效果图:
在这里插入图片描述

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

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

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        ...
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

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

<?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"
    android:fitsSystemWindows="true"
    tools:context=".activity.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_210"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:contentScrim="?attr/colorPrimary"
            android:fitsSystemWindows="true"
            app:popupTheme="@style/AppTheme.PopupOverlay">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@drawable/test_image_view"
                app:layout_collapseMode="parallax"/>
     ...

效果图:
在这里插入图片描述

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

还是看代码,看效果图:

<?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"
    android:fitsSystemWindows="true"
    tools:context=".activity.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dimen_210"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            app:contentScrim="?attr/colorPrimary"
            android:fitsSystemWindows="true"
            app:popupTheme="@style/AppTheme.PopupOverlay">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                android:src="@drawable/test_image_view"
                app:layout_collapseMode="parallax"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                app:layout_collapseMode="pin"/>

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

    <FrameLayout
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/record_collect_main_fragment_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dimen_8"
        app:rippleColor="@color/red_pressed"
        app:layout_anchor="@id/appBarLayout"
        app:layout_anchorGravity="bottom|end"
        app:srcCompat="@android:drawable/ic_input_add"/>
</android.support.design.widget.CoordinatorLayout>

效果图:

在这里插入图片描述

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

上代码:
fragment.layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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=".fragment.mvp.view.imp.MainView">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="@color/red_normal"
        app:tabTextColor="@android:color/darker_gray"
        app:tabSelectedTextColor="@color/white"/>

    <com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:id="@+id/record_list_refresh_layout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabs">

        <com.scwang.smartrefresh.layout.header.ClassicsHeader
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:srlAccentColor="@color/white"
            app:srlPrimaryColor="@color/red_pressed"/>

        <com.ecg.rencarehealth.recordcollect.view.SwapMenuRecyclerView.SwapRecyclerView
            android:id="@+id/record_list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"/>
    </com.scwang.smartrefresh.layout.SmartRefreshLayout>
</android.support.constraint.ConstraintLayout>

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

...
val tabLayout = view.findViewById<TabLayout>(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值