在許多App中看到, toolbar有收縮和擴展的效果, 例如:

要實現這樣的效果, 需要用到:
CoordinatorLayout和 AppbarLayout的配合, 以及實現了 NestedScrollView的布局或控件.
AppbarLayout是一種支持響應滾動手勢的app bar布局, CollapsingToolbarLayout則是專門用來實現子布局內不同元素響應滾動細節的布局.
與AppbarLayout組合的滾動布局(RecyclerView, NestedScrollView等),需要設置 app:layout_behavior = "@string/appbar_scrolling_view_behavior" .沒有設置的話, AppbarLayout將不會響應滾動布局的滾動事件.
我們回到再前面一章"Toolbar的使用", 將布局改動如下:
<?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="com.truly.mytoolbar.MainActivity"> <android.support.design.widget.AppBarLayout 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="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:layout_scrollFlags="scroll" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:title="Title" /> </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:id="@+id/tv_content" android:layout_margin="16dp" android:layout_width="match_parent" android:layout_height="match_parent" android:lineSpacingMultiplier="2" android:text="@string/textContent" /> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>
先看下效果再來解釋為什么.

可以看到,
- 隨着文本往上滾動, 頂部的toolbar也往上滾動, 直到消失.
- 隨着文本往下滾動, 一直滾到文本的第一行露出來, toolbar也逐漸露出來
解釋:
從上面的布局中可以看到, 其實在整個父布局CoordinatorLayout下面, 是有2個子布局
- AppbarLayout
- NestedScrollView
NestedScrollView先放一放, 我們來看AppbarLayout.
AppBarLayout 繼承自LinearLayout,布局方向為垂直方向。所以你可以把它當成垂直布局的LinearLayout來使用。AppBarLayout是在LinearLayou上加了一些材料設計的概念,它可以讓你定制當某個可滾動View的滾動手勢發生變化時,其內部的子View實現何種動作。
注意:
上面提到的"某個可滾動View", 可以理解為某個ScrollView. 就是說,當某個ScrollView發生滾動時,你可以定制你的“頂部欄”應該執行哪些動作(如跟着一起滾動、保持不動等等)。
這里某個ScrollView就是NestedScrollView或者實現了NestedScrollView機制的其它控件, 如RecyclerView. 它有一個布局行為Layout_Behavior:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
這是一個系統behavior, 從字面意思就可以看到, 是為appbar設置滾動動作的一個behavior. 沒有這個屬性的話, Appbar就是死的, 有了它就有了靈魂.
我們可以通過給Appbar下的子View添加app:layout_scrollFlags來設置各子View執行的動作. scrollFlags可以設置的動作如下:
(1) scroll: 值設為scroll的View會跟隨滾動事件一起發生移動。就是當指定的ScrollView發生滾動時,該View也跟隨一起滾動,就好像這個View也是屬於這個ScrollView一樣。
上面這個效果就是設置了scroll之后的.
(2) enterAlways: 值設為enterAlways的View,當任何時候ScrollView往下滾動時,該View會直接往下滾動。而不用考慮ScrollView是否在滾動到最頂部還是哪里.
我們把layout_scrollFlags改動如下:
app:layout_scrollFlags="scroll|enterAlways"
效果如下:

(3) exitUntilCollapsed:值設為exitUntilCollapsed的View,當這個View要往上逐漸“消逝”時,會一直往上滑動,直到剩下的的高度達到它的最小高度后,再響應ScrollView的內部滑動事件。
怎么理解呢?簡單解釋:在ScrollView往上滑動時,首先是View把滑動事件“奪走”,由View去執行滑動,直到滑動最小高度后,把這個滑動事件“還”回去,讓ScrollView內部去上滑。
把屬性改下再看效果
<android.support.v7.widget.Toolbar ... android:layout_height="?attr/actionBarSize" android:minHeight="20dp" app:layout_scrollFlags="scroll|exitUntilCollapsed" />

(4) enterAlwaysCollapsed:是enterAlways的附加選項,一般跟enterAlways一起使用,它是指,View在往下“出現”的時候,首先是enterAlways效果,當View的高度達到最小高度時,View就暫時不去往下滾動,直到ScrollView滑動到頂部不再滑動時,View再繼續往下滑動,直到滑到View的頂部結束
這個得把高度加大點才好實驗. 來看:
<android.support.v7.widget.Toolbar
...
android:layout_height="200dp" android:minHeight="?attr/actionBarSize" app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed" </android.support.design.widget.AppBarLayout>

Attention:
其實toolbar的默認最小高度minHeight就是"?attr/actionBarSize" , 很多時候可以不用設置. 而且從圖上可以看出, 其實這里有個缺陷, 就是title的位置和toolbar上的圖標行脫離了, 即使在布局里添加了 android:gravity="bottom|start", 在toolbar滾動的時候, title還在, 圖標滾動到隱藏了.

后面講解的CollapsingToolbarLayout可以解決這個問題, 這里先丟出來.
(5) snap:簡單理解,就是Child View滾動比例的一個吸附效果。也就是說,Child View不會存在局部顯示的情況,滾動Child View的部分高度,當我們松開手指時,Child View要么向上全部滾出屏幕,要么向下全部滾進屏幕,有點類似ViewPager的左右滑動

引入CollapsingToolbarLayout
CollapsingToolbarLayout是用來對Toolbar進行再次包裝的ViewGroup,主要是用於實現折疊(其實就是看起來像伸縮~)的App Bar效果。它需要放在AppBarLayout布局里面,並且作為AppBarLayout的直接子View。CollapsingToolbarLayout主要包括幾個功能(參照了官方網站上內容,略加自己的理解進行解釋):
(1) 折疊Title(Collapsing title):當布局內容全部顯示出來時,title是最大的,但是隨着View逐步移出屏幕頂部,title變得越來越小。你可以通過調用setTitle方法來設置title。
(2)內容紗布(Content scrim):根據滾動的位置是否到達一個閥值,來決定是否對View“蓋上紗布”。可以通過setContentScrim(Drawable)來設置紗布的圖片. 默認contentScrim是colorPrimary的色值
(3)狀態欄紗布(Status bar scrim):根據滾動位置是否到達一個閥值決定是否對狀態欄“蓋上紗布”,你可以通過setStatusBarScrim(Drawable)來設置紗布圖片,但是只能在LOLLIPOP設備上面有作用。默認statusBarScrim是colorPrimaryDark的色值.
(4)視差滾動子View(Parallax scrolling children): 子View可以選擇在當前的布局當時是否以“視差”的方式來跟隨滾動。(PS:其實就是讓這個View的滾動的速度比其他正常滾動的View速度稍微慢一點)。將布局參數app:layout_collapseMode設為parallax
(5)將子View位置固定(Pinned position children):子View可以選擇是否在全局空間上固定位置,這對於Toolbar來說非常有用,因為當布局在移動時,可以將Toolbar固定位置而不受移動的影響。 將app:layout_collapseMode設為pin。
我們來更改一下布局:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout ...> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="150dp"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_collapseMode="parallax" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:title="Title" /> </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"> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="16dp" android:lineSpacingMultiplier="2" android:text="@string/textContent" /> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>
可以看到, 我們把原本屬於toolbar的幾個屬性移到了CollapsingToolbarLayout上. 分別是:
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
同時給toolbar增加了一個折疊模式屬性
app:layout_collapseMode="parallax"
我們來看下效果:

嗯嗯, 折疊模式不對, toolbar的頂部圖標沒了. 我們改下折疊模式:
app:layout_collapseMode="pin"
再看效果:

我們把scrollFlags屬性改下, 看下對比:
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"

效果還是蠻不錯的, 有了點Google Material Design的感覺了.
上面說CollapsingToolbarLayout是個ViewGroup, 那么肯定還可以添加控件. 那么我們在里面添加一個ImageView來看看. 更改布局如下:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout ...> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="200dp"> <android.support.design.widget.CollapsingToolbarLayout ... android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/darkbg" app:layout_collapseMode="parallax" /> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_collapseMode="pin" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:title="Title" /> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView ... app:layout_behavior="@string/appbar_scrolling_view_behavior"> <TextView ... /> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>
來看下效果:

嗯, 有了點意思, 但不美觀, 上部的toolbar和圖片不協調. toolbar應該有默認的背景屬性, 我們去掉它看看.
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:title="Title" />
再看下效果:

這次真的不錯哦, 已經和很多大公司的app相像了. 但是為什么去掉toolbar的background就可以得到透明背景呢? 說句實話, 沒找到原因.
不過我們沒有給CollapsingToolbarLayout設置contentScrim屬性哦, 給它加個屬性看看.
<android.support.design.widget.CollapsingToolbarLayout ... app:contentScrim="?attr/colorPrimary" ...>

嗯嗯, 好像還不如沒設置這個屬性好呢.
什么時候需要contentScrim屬性呢?
因為這個布局里面給CollapsingToolbarLayout的layout_scrollFlags設置的是 "scroll|enterAlways|enterAlwaysCollapsed" , toolbar會全部消失的, 所以感覺不是很美觀. 如果將layout_scrollFlags屬性改為 "scroll|exitUntilCollapsed" , 效果會好點, 適合toolbar還是需要展示的場合.

不管怎么樣, 先去掉contentScrim屬性吧.
目前有很多APP比較喜歡采用沉浸式設計, 簡單點說就是將狀態欄和導航欄都設置成透明或半透明的.
我們來把狀態欄statusBar設置成透明. 在style主題中的AppTheme里增加一條:
<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 ... android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout ... android:fitsSystemWindows="true"> <android.support.design.widget.CollapsingToolbarLayout ... android:fitsSystemWindows="true"> <ImageView ... android:fitsSystemWindows="true" /> <android.support.v7.widget.Toolbar ... /> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView ...> <TextView ... /> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>
最后來看下效果:

其實還可以在CollapsingToolbarLayout里設置statusBarScrim為透明色, 不過有點問題, 最頂部的toolbar沒有完全隱藏, 還留了一點尾巴.

難道就這個屬性就沒用嗎? 我們把layout_scrollFlags改成 "scroll|exitUntilCollapsed" 看看:

這個時候toolbar不用隱藏, 所以還是美美的.
AppbarLayout整個做成沉浸式之后, 狀態欄的圖標可能會受到封面圖片顏色過淺的影響, 可以給其加一個漸變的不透明層.
漸變遮罩設置方法:
在res/drawable文件夾下新建一個名為status_gradient的xml資源文件, 代碼如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:angle="270" android:endColor="@android:color/transparent" android:startColor="#CC000000" /> <!-- shape節點中, 可以通過android:shape來設置形狀, 默認是矩形. gradient節點中angle的值270是從上到下,0是從左到右,90是從下到上。 此處的效果就是從下向上, 顏色逐漸由純透明慢慢變成黑透色--> </shape>
布局中, 在ImageView下面增加一個View, 背景設為上面的漸變遮罩.
<!-- 在頂部增加一個漸變遮罩, 防止出現status bar 狀態欄看不清 --> <View android:layout_width="match_parent" android:layout_height="40dp" android:background="@drawable/status_gradient" app:layout_collapseMode="pin" android:fitsSystemWindows="true" />
給遮罩設置折疊模式: app:layout_collapseMode="pin" , 折疊到頂部后定住. 來看下效果.


上圖是展開狀態的對比, 后面的是沒有添加遮罩的效果, 前面是添加了遮罩的效果. 下圖是添加了遮罩折疊后的效果. 有點黑暗系影片的感覺哦.
FloatingActionButton再次表演
作為Google Material Design的一個重要控件, FloatingActionButton怎么可能不在AppbarLayout中起點作用呢. 我們在布局中加一個懸浮按鈕, 讓它的錨點掛載Appbar的右下角. 這樣這個懸浮按鈕就和Appbar關聯起來了.
<android.support.design.widget.CoordinatorLayout ...> <android.support.design.widget.AppBarLayout ... </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView ... </android.support.v4.widget.NestedScrollView> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@drawable/ic_share_white_24dp" android:elevation="4dp" app:pressedTranslationZ="16dp" app:rippleColor="@android:color/white" app:layout_anchor="@id/appbar" app:layout_anchorGravity="bottom|end"/> </android.support.design.widget.CoordinatorLayout>
我們來看下效果.

好吧, 美美的Toolbar完成了, 有點Google Material Design撲面而來的感覺了.
這篇文章已經很長了, 還有些內容就不放進來了, 后面陸續完善.
借鑒了很多資料, 寫的時候忘了記錄下來, 如對您有損, 請聯系我進行刪除或更改. 致歉!
作者:朋朋彭哥
鏈接:https://www.jianshu.com/p/bbc703a0015e
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。