看似複雜的沉浸式體驗設計,其實也就是在處理以下兩個 System UI與用戶佈局(setContentView)之間說不清理還亂的關係:
- StatusBar 系統狀態欄
- NavigationBar 系統導航欄
網上類似“沉浸式狀態欄”的文章一搜一大把,且先不吐槽所謂的“沉浸式狀態欄”說法正確與否,隨便一篇文章都會告訴你,要實現沉浸式體驗有兩個關鍵地方:
window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_STABLE
window.statusBarColor = Color.TRANSPARENT
運行代碼,果然實現了“沉浸式狀態欄”的效果。可是我相信大多數人心裏是迷糊的:
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
和SYSTEM_UI_FLAG_LAYOUT_STABLE
是什麼?- 爲什麼要設置狀態欄顏色透明?設置其他顏色不可以麼?
以及接下來可能碰到的各種“沉浸式狀態欄”帶來的坑:
- Bull shit!用戶佈局 與 狀態欄信息 重疊了
- 可交互按鈕被手機的“劉海”給擋住了
- 狀態欄上時間等信息的文字顏色是黑色,搭配上狀態欄藍色的背景,好醜啊
在講解 沉浸式體驗的實現 與 異形屏 適配前,我們需要先補下課,瞭解些微的基礎知識:
各種Flag
STATUS_BAR_HIDDEN
/**
* Android 3.0(API 11)加入
*
* 已被廢棄,現等同於 SYSTEM_UI_FLAG_LOW_PROFILE
*/
public static final int STATUS_BAR_HIDDEN = SYSTEM_UI_FLAG_LOW_PROFILE;
STATUS_BAR_VISIBLE
/**
* Android 3.0(API 11)加入
*
* 已被廢棄,現等同於 SYSTEM_UI_FLAG_VISIBLE
*/
public static final int STATUS_BAR_VISIBLE = SYSTEM_UI_FLAG_VISIBLE;
SYSTEM_UI_FLAG_VISIBLE
/**
* Android 4.0(API 14)加入
*
* 表明設置系統UI(status bar等)爲可見狀態,
* 這也是默認效果。
*/
public static final int SYSTEM_UI_FLAG_VISIBLE = 0;
SYSTEM_UI_FLAG_LOW_PROFILE
/**
* Android 4.0(API 14)加入
*
* 低配模式,這裏的低配不是傳統意義上的低配置,
* 我更傾向於稱其爲低調模式,設置系統UI進入不顯眼的“低配置”模式。
* 具體表現爲:狀態欄 和/或 導航欄某些圖標 可能變暗
*/
public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001;
SYSTEM_UI_FLAG_HIDE_NAVIGATION
/**
* Android 4.0(API 14)加入
*
* 如果設置了 SYSTEM_UI_FLAG_LOW_PROFILE 後還是覺得系統UI顯眼,
* 不妨嘗試設置該Flag請求系統導航欄暫時隱藏,
* 這將導致基本的導航控件(對於國內用戶來說就是Android的三大金剛鍵)消失
* 這對於充分使用設備屏幕極爲有用,搭配 FLAG_LAYOUT_IN_SCREEN 食用效果更佳~
*
* Tip:Google 認爲導航控件十分重要,因此該Flag只會暫時性隱藏導航控件
* 用戶的任何行爲都會使導航欄控件重新出現,並 clear 掉該標誌位
*/
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
/**
* Android 4.1(API 16)加入
*
* 與 SYSTEM_UI_FLAG_HIDE_NAVIGATION 相比多了一個 LAYOUT 關鍵字,
* 不同的是,使用該 Flag 並不會隱藏 system UI,
* 而是使用戶的佈局延伸至 system UI 下方,
* 導致用戶的佈局被覆蓋掉(可使用fitSystemWindows(Rect)方法規避該問題)
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
SYSTEM_UI_FLAG_FULLSCREEN
/**
* Android 4.1(API 16)加入
*
* 全屏模式,此模式用戶的內容視圖將覆蓋整個屏幕,
* 這就意味着非關鍵的屏幕裝飾(例如狀態欄,不包括導航欄,Google認爲導航欄是非常重要的系統UI)將被隱藏
* 與 WindowManager.LayoutParams.FLAG_FULLSCREEN 不同的是:
*
* 1.如果與 Window#FEATURE_ACTION_BAR_OVERLAY 一同使用,則 ActionBar 也會一同隱藏
* 2.該FLAG更易清除:與 系統UI交互(下拉顯示通知) 或 跳轉至其他應用 都會清除該FLAG
*/
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
/**
* Android 4.1(API 16)加入
*
* 同 SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 與 SYSTEM_UI_FLAG_HIDE_NAVIGATION 的區別一樣,
* 該 FLAG 與 SYSTEM_UI_FLAG_FULLSCREEN 區別就是該 FLAG 會使用戶佈局延伸至系統狀態欄下方
* 導致用戶的佈局被系統狀態欄覆蓋掉(可使用fitSystemWindows(Rect)方法規避該問題)
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
SYSTEM_UI_FLAG_LAYOUT_STABLE
/**
* Android 4.1(API 16)加入
*
* 該標誌位通常與其他FLAG一同使用,表明系統期望View的insets是固定不變的
* 這意味着即使當前 window 的 Flag 變化影響了系統UI的佈局,
* 用戶界面的位置也並不會發生變化。
*
* 單獨使用該FLAG界面不會有任何變化。
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
SYSTEM_UI_FLAG_IMMERSIVE
/**
* Android 4.4(API 19)加入
*
* 該FLAG是用來修飾 SYSTEM_UI_FLAG_HIDE_NAVIGATION,
* 如果我們單獨使用 SYSTEM_UI_FLAG_HIDE_NAVIGATION,
* 那麼用戶與系統任何交互都會清除掉 SYSTEM_UI_FLAG_HIDE_NAVIGATION 這個FLAG
* 搭配該FLAG後則不會清除掉,除非從屏幕頂部向下滑動。
*/
public static final int SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800;
SYSTEM_UI_FLAG_IMMERSIVE_STICKY
/**
* Android 4.4(API 19)加入
*
* 該FLAG與 SYSTEM_UI_FLAG_IMMERSIVE 不同的是
* SYSTEM_UI_FLAG_IMMERSIVE 僅用來修飾 SYSTEM_UI_FLAG_HIDE_NAVIGATION,
* SYSTEM_UI_FLAG_IMMERSIVE_STICKY 是用來修飾 SYSTEM_UI_FLAG_HIDE_NAVIGATION 與
* SYSTEM_UI_FLAG_FULLSCREEN 這兩個Flag的。
*
* 還有一點不同的是,該FLAG在與系統UI交互後,會被短暫的clear,
* 一會兒自動恢復。
*/
public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
System UI 顏色
按照時間跨度來講,Android 系統的 System UI 經歷了以下幾個階段:
Android 4.4,可以設置狀態欄或者導航欄的背景爲透明
從Android 4.4開始,Android 系統纔開始了對 System UI 顏色的改造之路,當然,Android 4.4只是小試牛刀,只允許開發者設置狀態欄或者導航欄背景透明,只需要在Application的Theme里加入以下代碼:
<style name="AppTheme" parent="Theme.AppCompat">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
該style在 Android 5.0 以上 和 以下的效果並不一樣:
5.0以下表現爲 一個漸變的半透明效果;
5.0以上取消了這個漸變效果。
Android 5.0,可以設置狀態欄或者導航欄的顏色了
得益於 Google 在 Android 5.0 推出的 Material Design
的設計語言,Android 開發者終於能夠將狀態欄和導航欄設置成任何你想要的顏色了,只需調用以下方法:
/**
* Android 5.0(API 21)加入
*
* 設置狀態欄的顏色爲 {@code color}。
* 值得注意的是,該方法與前面提到的“透明狀態欄”是衝突的,
* 這意味着,如果設置了“透明狀態欄”,
* 再調用該方法無效。
*/
public abstract void setStatusBarColor(@ColorInt int color);
/**
* Android 5.0(API 21)加入
*
* 設置導航欄的顏色爲 {@code color}。
* 值得注意的是,該方法與前面提到的“透明導航欄”是衝突的,
* 這意味着,如果設置了“透明導航欄”,
* 再調用該方法無效。
*/
public abstract void setNavigationBarColor(@ColorInt int color);
Android 6.0,可以設置狀態欄中的圖標、字體顏色了
Android 中系統狀態欄中的字體、圖標的顏色默認爲淺色系(白色),如果App狀態欄設計爲淺色調的話,很有可能導致用戶看瞎了眼也看不清楚狀態欄的各種信息。這個尷尬的點一直到Android 6.0之後才得以解決。
Android 6.0加入了更改狀態欄內容顏色風格的Flag:
/**
* Android 6.0(API 23)加入
*
* 設置狀態欄文字圖標以“Light”模式繪製,
* 通俗的講,就是狀態欄文字圖標顏色變爲黑色。
*/
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
亦或者是通過在Style中進行如下設置來達到同樣的效果:
<style name="AppTheme" parent="Theme.AppCompat">
<item name="android:windowLightStatusBar">true</item>
</style>
知道了上面這些,我們可以做...
充分了解上面介紹的 Android 各種 SystemUI 的特性後,我們就可以實現市面上大多數應用所謂的沉浸式體驗
了。下面我們就來看幾個🌰。
頁面含有複雜背景/紋理
類似於QQ音樂歌曲播放詳情頁面
這種含有複雜的背景以及紋理的頁面,市面上常見的UI設計就是設置狀態欄透明,同時使用戶佈局侵入狀態欄下方。
代碼如下:
private fun translucent(){
supportActionBar?.hide()
val decorView = window?.decorView
decorView?.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
window?.statusBarColor = Color.TRANSPARENT
}
效果如下:
用戶佈局頂部爲純色背景
而像支付寶這種用戶佈局頂部爲純色背景的設計,常見思路就是將statusbar
的顏色設爲同色且用戶佈局不侵佔狀態欄即可。
代碼如下:
private fun translucent(){
supportActionBar?.hide()
window?.statusBarColor = Color.TRANSPARENT
}
設置完狀態欄顏色後,不要忘記根據狀態欄的顏色深淺設置狀態欄圖標對應的深色/淺色模式:
decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
狀態欄淺色模式:對應的狀態欄圖標變爲深色(黑色),不設置即爲深色模式,對應的狀態欄圖標爲淺色(白色)。
類電子書閱讀器
類似於微信閱讀
這種,用戶希望在閱讀電子書籍時能夠全情投入不受系統狀態欄信息變化的影響同時又能夠隨時呼出系統狀態欄以及App的菜單。like this:
這種效果的實現很顯然就是要動態更改decorView
的systemUiVisibility
了。如果你對上面介紹的各種system ui flag
瞭然於胸的話,一定能夠不加思索的寫出下面的代碼:
/**
* 根據傳入的visible參數動態改變decorView的systemUiVisibility
*
* @param 是否展示 status bar
*/
private fun changeSystemUI(visible: Boolean){
var newVis = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
llActionBar.visibility = View.VISIBLE
if (!visible) {
newVis =
newVis or (ImageView.SYSTEM_UI_FLAG_LOW_PROFILE or ImageView.SYSTEM_UI_FLAG_FULLSCREEN
or ImageView.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
llActionBar.visibility = View.GONE
}
val decorView = window?.decorView
decorView?.systemUiVisibility = newVis
}
效果如下:
總結
這篇只是簡單介紹了下Android View
體系中的system ui
的基礎,關於Android P
中的挖孔屏(notch)相關適配知識以及國內種類繁多的定製room系統(MIUI、EMOTION UI、Fly Me...)的system ui以及挖孔屏的適配,受制於篇幅,暫時就講這麼多了,下方有福利領取;
福利羣領取: