Android MaterialDesign系列之CoordinatorLayout实现折叠式菜单栏

1、简述

本文主要是对简易的折叠式菜单栏作个记录,方便之后开发进行比照改进。本文记录的有简单的Material化的折叠式菜单栏的实现,也将呈现一些比较重要的概念,好比 fitSystemWindows 的一些细节。java

2、效果

3、使用

一、CoordinatorLayout

CoordinatorLayout即为协调性布局,是一个增强版的FrameLayout,它能够协调子布局及控件,要使用它必须把它设置为整个布局的根ViewGroup。同时,能够经过对“特殊子控件”设置app:appbar_scrolling_view_behavior属性的值,进而来协调子控件在整个布局中的显示位置。须要注意的是这里的“特殊子控件”只有RecyclerViewViewPagerNewstedScrollView这三种。android

二、AppBarLayout

AppBarLayout是一个垂直的LinearLayout,实现了Material Design中AppBar的scrolling gestures特性。AppBarLayout的子View应该声明想要具备的“滚动行为”,这能够经过layout_scrollFlags属性或是setScrollFlags()方法来指定。 
AppBarLayout只有做为CoordinatorLayout的直接子View时才能正常工做, 为了让AppBarLayout可以知道什么时候滚动其子View,咱们还应该在CoordinatorLayout布局中提供一个可滚动View,咱们称之为scrolling view。scrolling view和AppBarLayout之间的关联,经过将scrolling view的Behavior设为AppBarLayout.ScrollingViewBehavior来创建。app

scrollFlags的属性值 做用
scroll 让AppBarLayout与scrolling view合为一体,当scrolling view滚动时,AppBarLayout也跟着一块儿滚动。这是“加强”Toolbar的一个必需取值,能够跟其余值一块儿使用,从而实现不一样的“加强”效果。单独使用scroll的话,其效果就相似给ListView加了一个HeaderView。

 ScrollFlags 标志介绍ide

<!--
          scrollFlags:
              scroll: 里面全部的子控件想要滑出屏幕的时候View都必须设置这个flag
                      没有设置flag的View将被固定在屏幕顶部
              enterAlways: 快速返回
              enterAlwaysCollapsed:当你的视图设置了minHeight属性的时候,那么视图只能以最小高度显示,只有当滚动视图达到顶部时才能扩大到完成高度
              exitUntilCollapsed: 滚动退出屏幕,最后折叠在顶端
        -->

scrollFlags效果展现:布局

动图展现app:layout_scrollFlags的5种滑动属性spa

三、CollapsingToolbarLayout

要实现上图的效果,须要跟AppBarLayout一块儿使用,CollapsingToolbarLayout的使用很简单,直接包裹Toolbar便可,其中能够增长一个ImageView控件来做为CollapsingToolbarLayout的“背景”,其实CollapsingToolbarLayout自己是一个FrameLayout,因此其子控件的摆放就是从左上角开始,一个个叠加起来。不过,其中的Toolbar默认一开始是隐藏的。.net

划重点设计

这里我必须得指出是fitSystemWindows是很重要的,这时候若是没有fitSystemWindows的话,要知道标题栏会覆盖上状态栏的,这其实不是咱们想要的。故而须要使用fitSystemWindows来告知标题栏不会被覆盖。3d


四、代码

<?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"
    android:background="@color/colorAccent"
    tools:context="com.example.mydairytestproject.JingActivity">
    <android.support.design.widget.AppBarLayout
        android:fitsSystemWindows="true"
        android:layout_width="match_parent"
        android:layout_height="250dp">
        <!-- AppBar固定宽高-->
        <android.support.design.widget.CollapsingToolbarLayout
            android:fitsSystemWindows="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <!-- app:contentScrim="?attr/colorPrimary" 折叠时的颜色-->
            <ImageView
                android:fitsSystemWindows="true"
                android:src="@mipmap/ic_weather_bg_day"
                android:layout_width="match_parent"
                android:scaleType="fitXY"
                android:layout_height="match_parent"
                app:layout_collapseMode="parallax"/>
            <!-- app:layout_collapseMode x轴上有必定的偏移-->
            <!-- ImageView的全部父布局及自己设置为fitSystem = true-->
            <android.support.v7.widget.Toolbar
                app:title="我是谁"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin">
            </android.support.v7.widget.Toolbar>
        </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">
        <LinearLayout
            android:background="@color/colorPrimary"
            android:layout_width="match_parent"
            android:layout_height="400dp">
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

如下是比较重要的,设置的是AppbarLayout 的滑动监听事件,能够经过使用滑动监听设置标题的透明度来达到一些效果,有必要记录如下的。 code

mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBar, int offset) {
                mTvTitle.setAlpha(Math.abs(offset * 1f / appBar.getTotalScrollRange()));
            }
        });

4. 分析 fitSystemWindows 属性

1. 初始化的效果展现:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context="com.example.mydairytestproject.views.RetrofitNetActivity">
    <ImageView
        android:id="@+id/img_bg"
        android:layout_width="match_parent"
        android:layout_height="220dp"
        android:layout_marginBottom="3dp"
        android:scaleType="centerCrop"
        android:background="@drawable/bg_person_head"
        android:visibility="visible" />
</LinearLayout>
// 基础的style
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

 好了,这就是初始化的效果展现。

2. 调整主题效果

style.xml
    <style name="picture_theme" parent="AppTheme">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

style.xml(v19)

    <style name="picture_theme" parent="AppTheme">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowIsTranslucent">false</item>
    </style>

能够发现确实已经顶上去了,通常这就是咱们想要的效果。 

3. 再开看看 fitSystemWindows 会有什么效果

比较不错的 fitSystemWindows

android:fitsSystemWindows=“true” 默认行为就是经过在 View 上设置和系统窗口同样高度的边框(padding )来确保你的内容不会出现到系统窗口下面。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    android:fitsSystemWindows="true"
    tools:context="com.example.mydairytestproject.views.RetrofitNetActivity">
    <ImageView
        android:id="@+id/img_bg"
        android:layout_width="match_parent"
        android:layout_height="220dp"
        android:layout_marginBottom="3dp"
        android:scaleType="centerCrop"
        android:background="@drawable/bg_person_head"
        android:visibility="visible" />
</LinearLayout>

如上所示,当无脑使用fitSystemWindows = "true"时,那么状态栏会空下来的。多注意该概念,是使得布局不占用系统布局空间。

设置fitSystemWindows=true以后, 设置在这个view上的padding都会失效。

小结:这里因为设置了状态栏为透明色,假如这时候设置布局那么就会直接将布局给顶到状态栏上去,对图固定的图片背景这显然是咱们想要的,可是对于文本效果,那么咱们仍然须要系统预留出这一部分,是得系统不占用系统的布局空间。

深刻分析fitSystemWindows属性

分析见:全屏、沉浸式、fitSystemWindow使用及原理分析:全方位控制“沉浸式”的实现

              从fitSystemWindows提及

 以下为系统默认的根布局样式:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    <!--关键点1-->
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>


 

个人相关系列文章:Android 沉浸式状态栏的设计与实现