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 沉浸式狀態欄的設計與實現