AppbarLayout的簡單用法實現收縮效果
ps:本文章為轉載文章,原文為:https://www.jianshu.com/p/bbc703a0015e
在許多App中看到, toolbar有收縮和擴充套件的效果, 例如:
appbar.gif
要實現這樣的效果, 需要用到:
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>
先看下效果再來解釋為什麼.
appbar_2.gif
可以看到,
- 隨著文字往上滾動, 頂部的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"
效果如下:
appbar_3.gif
(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"
/>
appbar_4.gif
(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>
appbar_5.gif
Attention:
其實toolbar的預設最小高度minHeight就是"?attr/actionBarSize" , 很多時候可以不用設定. 而且從圖上可以看出, 其實這裡有個缺陷, 就是title的位置和toolbar上的圖示行脫離了, 即使在佈局裡添加了 android:gravity="bottom|start", 在toolbar滾動的時候, title還在, 圖示滾動到隱藏了.
image.png
後面講解的CollapsingToolbarLayout可以解決這個問題, 這裡先丟出來.
(5) snap:簡單理解,就是Child View滾動比例的一個吸附效果。也就是說,Child View不會存在區域性顯示的情況,滾動Child View的部分高度,當我們鬆開手指時,Child View要麼向上全部滾出螢幕,要麼向下全部滾進螢幕,有點類似ViewPager的左右滑動
appbar_6.gif
引入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"
我們來看下效果:
appbar_8.gif
嗯嗯, 摺疊模式不對, toolbar的頂部圖示沒了. 我們改下摺疊模式:
app:layout_collapseMode="pin"
再看效果:
appbar_9.gif
我們把scrollFlags屬性改下, 看下對比:
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
appbar_10.gif
效果還是蠻不錯的, 有了點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>
來看下效果:
appbar_11.gif
嗯, 有了點意思, 但不美觀, 上部的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" />
再看下效果:
appbar_12.gif
這次真的不錯哦, 已經和很多大公司的app相像了. 但是為什麼去掉toolbar的background就可以得到透明背景呢? 說句實話, 沒找到原因.
不過我們沒有給CollapsingToolbarLayout設定contentScrim屬性哦, 給它加個屬性看看.
<android.support.design.widget.CollapsingToolbarLayout
...
app:contentScrim="?attr/colorPrimary"
...>
appbar_13.gif
嗯嗯, 好像還不如沒設定這個屬性好呢.
什麼時候需要contentScrim屬性呢?
因為這個佈局裡面給CollapsingToolbarLayout的layout_scrollFlags設定的是 "scroll|enterAlways|enterAlwaysCollapsed" , toolbar會全部消失的, 所以感覺不是很美觀. 如果將layout_scrollFlags屬性改為 "scroll|exitUntilCollapsed" , 效果會好點, 適合toolbar還是需要展示的場合.
appbar_14.gif
不管怎麼樣, 先去掉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>
最後來看下效果:
appbar_15.gif
其實還可以在CollapsingToolbarLayout裡設定statusBarScrim為透明色, 不過有點問題, 最頂部的toolbar沒有完全隱藏, 還留了一點尾巴.
image.png
難道就這個屬性就沒用嗎? 我們把layout_scrollFlags改成 "scroll|exitUntilCollapsed" 看看:
image.png
這個時候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" , 摺疊到頂部後定住. 來看下效果.
image.png
image.png
上圖是展開狀態的對比, 後面的是沒有新增遮罩的效果, 前面是添加了遮罩的效果. 下圖是添加了遮罩摺疊後的效果. 有點黑暗系影片的感覺哦.
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>
我們來看下效果.
appbar_16.gif
好吧, 美美的Toolbar完成了, 有點Google Material Design撲面而來的感覺了.
這篇文章已經很長了, 還有些內容就不放進來了, 後面陸續完善.
作者:朋朋彭哥
連結:https://www.jianshu.com/p/bbc703a0015e
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。