刨根問底-論Android“沉浸式”

lybm9884 8年前發布 | 8K 次閱讀 安卓開發 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

 

 本文由用戶 lybm9884 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!