Android 狀態列操作,你想知道的都在這裡了

亦楓發表於2017-02-22

一直以來,iOS 裝置上狀態列背景色和圖示文字顏色的靈活可變性始終受到設計人員的青睞,有意地恰當地融入到 App 的各種介面設計當中,更好地提升使用者體驗。

由於系統的限制,在老版本的安卓系統中,Android App 無法做到這些,產生一些設計上的遺憾。幸運的是,自 4.4 版本(API 19)以後,Android 系統開始支援狀態列的定製,並被納入 Android 設計規範當中,Android App 在設計上邁出了重要的一步。

“沉浸式狀態列” VS “透明狀態列”?


一般來說,Android 預設的狀態列樣式表現為黑底白字,如果我們應用的標題欄背景色也為黑色,那就能與狀態列很好地銜接在一起,體驗極佳。反之,如果為其他的顏色,整個介面的呈現效果就會大打折扣。

幸運的是,Android 4.4 版本開始,系統提供了相應的 API,支援狀態列全透明化,介面 Content View 可以延伸到狀態列上,填充狀態列背景色。而在 Android 5.0 版本開始,系統在此基礎上做了進一步優化和規範,能夠實現動態改變狀態列背景色,在透明度上預設呈現為半透明化,可定製化程度更高。

在此基礎上,最終要做到我們的應用呈現在 Android 各個系統版本上的效果如圖所示:

Android 狀態列操作,你想知道的都在這裡了
Status Bar

關於 Android 4.4 版本開始的狀態列變化,許多人喜歡稱之為“沉浸式狀態列”,但從系統提供的 API 命名上可以看出,核心詞彙為 “Translucent”,故準確來講,這種效果又應該稱之為“透明狀態列”。知乎上對於這兩種叫法也頗有爭議,具體內容可參考話題:為什麼在國內會有很多使用者把「透明欄」(Translucent Bars)稱作 「沉浸式頂欄」?。可能對於設計師而言,沉浸式還是透明式的稱呼有所區別,但對於廣大開發者而言,無足輕重,我們所關注的應該是如何實現這種效果,並能夠很好的相容到各個版本中。

相關 API 介紹


一般來說,目前在 Android 專案中我們都會使用 Toolbar 替代 ActionBar 來實現導航欄,除此之外,要實現透明狀態列效果,還需要了解兩個相關 API,下面逐一介紹一下:

一:

<item name="android:windowTranslucentStatus">true</item>複製程式碼

也可以在程式碼中實現(據說,在程式碼中實現相容性更好,style 資源中設定的方式在某些國產手機廠商定製的系統中存在一些問題):

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
    local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
}複製程式碼

顧名思義,該屬效能夠實現透明狀態列效果,是在 Android 4.4 版本引入的,也就是相容至 API 19 及以上版本。使用該屬性設定主題後,內容佈局向上延伸至狀態列,並且在不同版本的系統中呈現效果也有所區別,如圖所示:

Android 狀態列操作,你想知道的都在這裡了

顯然,在 API 19 及更高版本上,Toolbar 內容延伸至狀態列上去了,出現重疊問題,此時,就需要使用到另一個屬性了。

二:

android:fitsSystemWindows="true"

Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity.

用在 layout 佈局檔案中。官方文件給出了很明確的介紹,大致是說能夠將使用該屬性的檢視與系統視窗(如狀態列)保持一定的 padding 間距。所以如果我們在 toolbar 中設定了該屬性,就能夠解決 <item name="android:windowTranslucentStatus">true</item> 配置帶來的檢視延伸問題,使呈現效果達到文章開始所示圖中的效果。

使用案例分析


res/values/styles 檔案中定義基礎主題樣式:

    <style name="BaseTheme" parent="Theme.AppCompat.Light.NoActionBar"/>
    <style name="AppTheme" parent="BaseTheme">
    </style>複製程式碼

res/values-v19/styles 檔案中定義相容主題樣式:

    <style name="AppTheme" parent="BaseTheme">
        <item name="android:windowTranslucentStatus">true</item>
    </style>複製程式碼

然後在 AndroidManifest.xml 檔案中使用全域性主題樣式:

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="Samples"
        android:supportsRtl="true"
        android:name=".MyApplication"
        android:theme="@style/AppTheme">

        ......複製程式碼

新建一個 layout 佈局檔案,單獨定義 toolbar 內容,在應用中的其他 Activity 介面佈局中使用 include 標籤潛入引用:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/tb_toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?actionBarSize"
    android:background="@color/colorPrimary"
    android:fitsSystemWindows="true"
    app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:title="@string/app_name"
    app:titleTextColor="@android:color/white">

</android.support.v7.widget.Toolbar>複製程式碼

這裡使用 android:fitsSystemWindows="true" 屬性解決內容試圖向上延伸的問題。實際上,也可以使用 android:paddingTop="@dimen/toolbar_padding_top" 的方式解決,toolbar_padding_top 間距為狀態列高度,在大多數機器上狀態列高度為 25dp,當然也可以通過程式碼動態獲取狀態列高度並設定到 Toolbar 的 paddingTop 屬性上。需要注意的是,這裡要做相容判斷,比如在 res/values/dimens.xml 中定義toolbar_padding_top 高度為 0dp,在 res/values-v19/dimens.xml 中為 25dp,確保相容 Android 4.4 以下版本。

基本上,做到這些就能夠實現文章開頭處圖中的效果。值得注意的是,有時候如果想在 Android 5.0 及以上版本的系統中也做到全透明效果,或者說狀態列與導航欄的顏色一致,還可以做進一步相容處理,畢竟自 5.0 版本開始,系統對於狀態列背景色的定製提供了更好的 API。如 res/values-v21/styles.xml 中定義:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="AppTheme" parent="BaseTheme">
        <item name="android:colorPrimary">@color/colorPrimary</item>
        <item name="android:colorPrimaryDark">@color/colorPrimary</item>
        <item name="android:colorAccent">@color/colorAccent</item>
    </style>

</resources>複製程式碼

說明一點,使用這種處理方式後,5.0 系統中應用的狀態列背景色可隨意定製,同時與使用 <item name="android:windowTranslucentStatus">true</item> 樣式有所不同的是,該處理方式不會引起內容檢視的向上延伸,所以不需要在 layout 佈局檔案中額外新增 android:fitsSystemWindows="true" 屬性。當然,新增了也無所謂,畢竟還要相容 4.4 到 5.0 之間的版本。

狀態列白底黑字


前面我們說過,雖然說 4.4 版本開始,可以實現透明狀態列效果,也就是可以通過各種手段實現修改狀態列背景色,但是狀態列圖示和文字的顏色預設為白色,這個是無法像 iOS 系統那樣,根據應用的整體色調動態修改。如果恰好 Toolbar 的背景色為白色,為了保持一致,將狀態列背景色調為白色的話,就會與狀態列的白色內容發生衝突,導致其內容無法凸顯,這個體驗肯定無法被使用者接受。

所以,遇見這種 Toolbar 或者說導航欄背景色為白色的情況,一般有兩種處理方式:第一種,不修改狀態列背景色,通常預設為黑色背景白色內容;第二種,修改狀態列背景色為淡黑色,這樣既能顯示狀態列內容,又能與白色導航欄弱顯銜接,比如支付寶 App 就是這麼做的:

Android 狀態列操作,你想知道的都在這裡了
Alipay Status Bar

像上圖這種處理方式較黑色狀態列來說,相對緩和一些,那能不能做到修改狀態列內容的顏色呢,比如白底黑字?大家知道,Android 系統是開源的,國內的各家手機廠商都做了一些自己的定製,像部分廠商定製的系統就提供了相應的 API 供開發人員做適配工作。比如,部分廠商就提供了相應的 API 來修改狀態列內容顏色,實現狀態列白底黑字效果,如圖:

Android 狀態列操作,你想知道的都在這裡了

具體做法就是,在程式碼中判斷系統型別,與提供修改狀態內容顏色的系統匹配,使用其特定的 API 操作即可。目前開放這種定製 API 的系統已知有 MIUI 和 Flyme 系統,具體實現程式碼可參考:

其實,在 Android 6.0(API 23)及更高的版本上,系統也開始提供了對應的 API 來實現淺色調背景的狀態列效果,可將狀態列圖示和文字內容改為黑色樣式,實現方式為:

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        }複製程式碼

但是不知國內廠商在定製系統時是否對此有做處理,能否相容這個 API ?不過,貌似微博 App 是這麼做的,大家感興趣地不妨一試。

注意事項


除了上述 Toolbar 與 狀態列在背景色上的銜接,App 中常見還有這樣一種設計,以微博個人主頁為例,如圖所示:

Android 狀態列操作,你想知道的都在這裡了

頁面頂部的圖片內容延伸至狀態列中,這種做法其實就是單獨使用 <item name="android:windowTranslucentStatus">true</item> 樣式,不在 layout 佈局檔案中新增 android:fitsSystemWindows="true" 屬性即可。

還有一點,通常我們會在資原始檔中定義不同版本的主題樣式,再在 <application> 標籤中統一設定,然後所有的 Activity 都能使用這個主題樣式。但存在這樣一種情況,Activity 由不同的 Fragment 組成,然後不同 Fragment 在狀態列的呈現上有所不同,比如有的 Fragment 頂部使用 Toolbar 與狀態列銜接,有的頂部直接使用圖片延伸至狀態列上甚至不會用到 Toolbar,如圖所示:

Android 狀態列操作,你想知道的都在這裡了

由於 Fragment 是無法像 Activity 那樣在 AndroidManifest.xml 中單獨設定主題樣式的,所以這裡可以這樣做:宿主 Activity 還是使用透明樣式,以滿足圖片延伸的 Fragment 頁面效果,其他使用 Toolbar 的 Fragment,在其 Layout 佈局檔案頂部單獨定義一個 View,讓其延伸至狀態列,然後在程式碼中根據不同版本系統設定其高度,4.4 版本以下設定該 View 高度為0,4.4 及以上版本設定為裝置狀態列高度,通過程式碼獲取狀態高度的方式如下:

    /**
     * 獲取狀態列高度
     * @param activity
     * @return
     */
    public static int getStatusBarHeight(Activity activity){
        Rect rect = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        return rect.top==0 ? 60 : rect.top;
    }複製程式碼

最後再補充一點,大家知道,長按 Toolbar 中 Menu Item 時會顯示一個 Toast 提示,內容來自 Item 定義時對應的 title 屬性,通常顯示如圖所示:

Android 狀態列操作,你想知道的都在這裡了

但是,如果你誤將 fitsSystemWindows 屬性設定在了 style 樣式檔案中,比如:

<style name="BaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:fitsSystemWindows">true</item>
</style>複製程式碼

將會導致下圖效果:

Android 狀態列操作,你想知道的都在這裡了

可見,Menu Options Item 長按時彈出的 Toast 樣式沒了內容間距,顯然很醜。所以,記得將該屬性設定在正確的佈局檔案中,不要設定成全域性的。

相關擴充


以上便是有關 Android 4.4 開始的狀態列背景色相關知識,可以看出 4.4 、5.0 、6.0 版本作為三個分水嶺,根據需要做好相關適配工作即可。還有一種通過 setSystemUiVisibility() 方法設定狀態列的方式,還能實現狀態列的顯示與隱藏互動效果,具體可參考這篇文章:

GitHub 上對於 Android 4.4 版本開始的狀態列背景色的處理有一個開源庫,感興趣地朋友也可借鑑參考一番,地址如下:

有關 Toolbar 替換 ActionBar 的使用,可以我之前總結的一篇文章,地址為:

歡迎關注我


本文由 亦楓 創作並首發於 亦楓的個人部落格 ,同步授權微信公眾號:技術鳥(NiaoTech),歡迎關注。

Android 狀態列操作,你想知道的都在這裡了

相關文章