「沉浸式」體驗?異形屏適配?我把他們扒光了明明白白告訴你應該這樣作

看似複雜的沉浸式體驗設計,其實也就是在處理如下兩個 System UI與用戶佈局(setContentView)之間說不清理還亂的關係:java

  • StatusBar 系統狀態欄
  • NavigationBar 系統導航欄

網上相似「沉浸式狀態欄」的文章一搜一大把,且先不吐槽所謂的「沉浸式狀態欄」說法正確與否,隨便一篇文章都會告訴你,要實現沉浸式體驗有兩個關鍵地方:android

  • window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or SYSTEM_UI_FLAG_LAYOUT_STABLE
  • window.statusBarColor = Color.TRANSPARENT

運行代碼,果真實現了「沉浸式狀態欄」的效果。但是我相信大多數人內心是迷糊的:微信

  • SYSTEM_UI_FLAG_LAYOUT_FULLSCREENSYSTEM_UI_FLAG_LAYOUT_STABLE 是什麼?
  • 爲何要設置狀態欄顏色透明?設置其餘顏色不能夠麼?

以及接下來可能碰到的各類「沉浸式狀態欄」帶來的坑:ide

  • 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里加入如下代碼:ui

<style name="AppTheme" parent="Theme.AppCompat"> <item name="android:windowTranslucentStatus">true</item> <item name="android:windowTranslucentNavigation">true</item> </style>
複製代碼

該style在 Android 5.0 以上 和 如下的效果並不同:this

5.0如下表現爲 一個漸變的半透明效果;spa

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 
  					or 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:

這種效果的實現很顯然就是要動態更改 decorViewsystemUiVisibility了。若是你對上面介紹的各類 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以及挖孔屏的適配,受制於篇幅,咱們放在下篇文章詳細介紹。

總之,

路漫漫其修遠兮,吾將上下而求索; 革命還沒有成功,同志仍需努力!

相關文章
相關標籤/搜索