刨根問底-論Android“沉浸式”
網上談論“沉浸式”的文章多的不可勝數,有人把 “沉浸式” 叫做 “沉浸式狀態欄” ,還有人稱作為 “透明狀態欄” 、 “變色狀態欄” 等。
這里我們先給出 “沉浸式” 的直觀感受,引用“知乎”網友的答案
使用帶虛擬鍵的手機才能明顯感覺到沉浸式所帶來的變化:狀態欄、導航欄隱藏。
而對于使用實體按鍵的手機的用戶來說, “沉浸式” 所帶來的變化僅僅是狀態欄隱藏,事實上,狀態欄隱藏在之前也很常見,各種國產應用啟動時都會隱藏狀態欄。
把上面的文字翻譯成圖片的話,標準的 “沉浸式” 大致是這個效果。
標準沉浸式.gif
App默認是全屏的,用戶可以從頂部或者底部“滑出”狀態欄和導航欄,一段時間后狀態欄和導航欄會自動消失。
嗯,沒錯,這個才是標準的 “沉浸式” 。
有了以上的定義,下面這兩種常見的效果,只能說改變了狀態欄的“樣式”(Translucent Bar倒是比較貼近)。但是,他們不是 “沉浸式” 。
非沉浸式.png
接下來,將進入本篇文章的主題,我們會從兩個方面去 “論沉浸式” 。
- 4.4之前,谷歌是如何提供類似 “沉浸式” 的體驗的
- 如何以最簡單的方式實現 “狀態欄” 與App “合二為一” 的效果(真個真的不叫 沉浸式 啦~)
setSystemUiVisibility
4.0之后(3.0也支持,這里暫時只討論手機平臺),官方提供了這個方法,可以改變系統UI的可見性,使用方法如下:
int flag = View.SYSTEM_UI_FLAG_FULLSCREEN;
getWindow().getDecorView().setSystemUiVisibility(flag);
多個值可使用“|”操作符,比如:
int flag = View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
getWindow().getDecorView().setSystemUiVisibility(flag);
系統提供了很多類似View.SYSTEM_UI_FLAG_xxx的常量,具體有下面幾種:
-
SYSTEM_UI_FLAG_FULLSCREEN(4.1+):隱藏狀態欄,手指在屏幕頂部往下拖動,狀態欄會再次出現且不會消失,另外activity界面會重新調整大小,直觀感覺就是activity高度有個變小的過程。
SYSTEM_UI_FLAG_FULLSCREEN.gif
-
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN(4.1+):配合SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN一起使用,效果使得狀態欄出現的時候不會擠壓activity高度,狀態欄會覆蓋在activity之上。
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN.gif
-
SYSTEM_UI_FLAG_HIDE_NAVIGATION(4.0+)
:會使得虛擬導航欄隱藏,但同樣用戶可以從屏幕下邊緣“拖出”且不會再次消失,同時activity界面會被擠壓。
SYSTEM_UI_FLAG_HIDE_NAVIGATION.gif
-
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION(4.1+):配合 SYSTEM_UI_FLAG_HIDE_NAVIGATION 一起使用,效果使得導航欄出現的時候不會擠壓activity高度,導航欄會覆蓋在activity之上。
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.gif
-
SYSTEM_UI_FLAG_LAYOUT_STABLE(4.1+):使用以上四個屬性,可以達到activity占據屏幕所有空間,同時狀態欄和導航欄可以懸浮在activity之上的效果。但是此時activity的內容也會(比如頂部和底部各有一個TextView)狀態欄和導航欄之下,當狀態欄和導航欄出現的時候,看起來會這樣:
1.gif
顯然,文字被遮蓋了我們是不能接受的,此時我們需要另外一個屬性, android:fitsSystemWindows=“true” ,這個屬性表示系統UI(狀態欄、導航欄)可見的時候,會給我們的布局加上padding(paddingTop、paddingBottom)屬性,這樣內容就不會被蓋住了。我們在activity的根布局加上這個屬性,效果如下
fitsSystemWindows.gif
我們發現,當狀態欄和導航欄出現的時候,內容會被擠壓一下,這個體驗也不是很理想,那么怎么解決這個問題呢?
其實,我們的內容本就不該占據狀態欄和導航欄的位置(游戲一般才會全屏,普通app的狀態欄一般都是透明而不是讓內容去占據這個空間,后面會介紹怎么實現狀態欄“透明效果”),我們需要加上SYSTEM_UI_FLAG_LAYOUT_STABLE這個屬性來解決這個問題
,效果如下
SYSTEM_UI_FLAG_LAYOUT_STABLE.gif
以上都是4.1(除了SYSTEM_UI_FLAG_HIDE_NAVIGATION
)的屬性,觀察之后我們發現,不管是那種屬性,狀態欄和導航欄總是會“遮擋”activity,為了解決這個問題,4.4引入了 “全屏沉浸模式” 這個概念。
-
SYSTEM_UI_FLAG_IMMERSIVE(4.4+):這個屬性是用來實現 “沉浸式” 效果的,官方稱作 “Immersive full-screen mode” 。
To provide your app with a layout that fills the entire screen, the new SYSTEM_UI_FLAG_IMMERSIVE flag for setSystemUiVisibility() (when combined with SYSTEM_UI_FLAG_HIDE_NAVIGATION ) enables a new immersive full-screen mode.
大意是SYSTEM_UI_FLAG_IMMERSIVE和 SYSTEM_UI_FLAG_HIDE_NAVIGATION一起使用,會帶來一個全新的 “全屏沉浸模式” 。我們使用這兩個屬性看下效果~
immersive1.gif
納尼?顯然和官方描述的占據整個屏幕有點不符合,狀態欄一直占據著空間(不知道為何官方這么描述~)。我們再加一個屬性SYSTEM_UI_FLAG_FULLSCREEN,我們再來看下效果
immersive2.gif
擠壓的效果如果你不滿意,加上SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,這會讓狀態欄和導航欄“懸浮”在activity之上,這個前面提到過,不做更多解釋,效果如下
immersive3.gif
-
SYSTEM_UI_FLAG_IMMERSIVE_STICKY:和SYSTEM_UI_FLAG_IMMERSIVE相似,它被稱作“粘性”的沉浸模式,這個模式會在狀態欄和導航欄顯示一段時間后,自動隱藏(你可以點擊一下屏幕,立即隱藏)。同時需要重點說明的是,這種模式下,狀態欄和導航欄出現的時候是“半透明”狀態,效果如下
immersive4.gif
我覺得官方要表達的 “Immersive full-screen mode” ,應該是這個意思才對吧?
慎用 “沉浸式” ,除非你真的需要這樣做。比如做一款游戲或者繪圖應用就很合適。
“Translucent Bar”
網上所謂的 “沉浸式狀態欄” ,這個概念官方從未提及過。我看了就大多數文章,最終表達的效果基本是這樣
預覽.png
為了對比,貼出了不同Android版本的效果(某Q也是如此),其實就是為了讓狀態欄看上起和我們應用的標題欄(當然,也可能是和整個背景)“合二為一”,不至于和之前一樣,看到的是“黑乎乎”的狀態欄,網上也有很多第三方庫來實現這樣的效果,比如: SystemBarTint 。
其實這個效果4.4以上實現很簡單,大概需要用到以下兩個屬性:
-
windowTranslucentNavigation:application的主題加上這個屬性,表示狀態欄半透明,另外,會使得狀態欄會懸浮在activity之上:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowTranslucentStatus">true</item> </style>
為了不遮擋activity內容,需要配合另外一個屬性
-
android:fitsSystemWindows:使用這個屬性的View,系統會在View頂部添加padding(大小為狀態欄高度):
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" android:background="@color/colorPrimary"> <!--嵌套的原因是因為LinearLayout會被加上paddingTop,導致TextView無法居中--> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="@android:color/white" android:textSize="20sp" android:text="這是標題"/> </RelativeLayout> </LinearLayout> </LinearLayout>
有些應用沒有標題,希望將背景和標題融為一體,比如頂部是“輪播”欄:
預覽2.png
其實也很簡單,布局稍微調整下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="150dp"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@drawable/bg"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="內容區域"/>
</LinearLayout>
總之一個原則是,你希望哪塊區域擴展到狀態欄,就給它加上
android:fitsSystemWindows="true"
另外,別忘了,主題要設置成狀態欄半透明
<item name="android:windowTranslucentStatus">true</item>
我們注意到, 狀態欄 在4.4-5.0之間的效果是全透明,5.0+是半透明,顏色的差別QQ等App也沒有特意去做處理,我也覺得沒必要,你覺得呢?
好了,希望本文能幫助你真正理解什么是 “沉浸式” 。
來自:http://www.jianshu.com/p/38c2239dd0d4