Android 狀態列透明

Cavabiao發表於2018-03-22

前言:最近專案大量用到狀態列透明,網上也出現很多庫可以直接拿來用,個人認為沒有必要那麼重引用到一個庫(有木有同學和我有一樣的想法),所以研究了一番,在此做個記錄加強記憶也便後期查閱,如果無意中有幸能幫助到你那就再好不過了。

一、兩個基本概念

Android 從 4.4 (SDK 19) 開始支援系統欄(狀態列+導航欄)半透明效果:

You can now make the system bars partially translucent with new themes, Theme.Holo.NoActionBar.TranslucentDecor and Theme.Holo.Light.NoActionBar.TranslucentDecor. By enabling translucent system bars, your layout will fill the area behind the system bars, so you must also enable fitsSystemWindows for the portion of your layout that should not be covered by the system bars.
If you're creating a custom theme, set one of these themes as the parent theme or include the windowTranslucentNavigation and windowTranslucentStatus style properties in your theme.

翻譯一下就是:

通過使用  Theme.Holo.NoActionBar.TranslucentDecor 或 Theme.Holo.Light.NoActionBar.TranslucentDecor 主題,可以實現系統欄半透明的效果。但是,這樣一來系統欄會覆蓋在佈局上(實際上是內容延伸到了系統欄原本所佔的區域下面),為了使佈局不被系統欄覆蓋,需要設定 fitsSystemWindows 屬性為 true。

TranslucentDecor主題設定了兩個屬性windowTranslucentStatuswindowTranslucentNavigation都為 true,前者指定狀態列半透明、後者指定導航欄半透明。

本文只探討“狀態列”

預設樣式是這樣:

預設樣式,4.4.4 系統頂部狀態列黑色,6.0.0 系統狀態列深色

可見 Toolbar 和系統狀態列之間有明顯的分界,我們要實現的效果是 Toolbar 和狀態列背景統一,看起來像是一個整體(自行腦補圖片)。

按照官方文件,我們自定義主題:

<style name="AppTheme.NoActionBar.TransparentStatusBar">
    <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item>
</style>
複製程式碼

對應的 Activity 引用該主題:

<activity
    android:name=".DetailActivity"
    android:label="@string/title_activity_detail"
    android:theme="@style/AppTheme.NoActionBar.TransparentStatusBar">
</activity>
複製程式碼

我看來看看效果:

加了 TranslucentStatusBar 主題,整個佈局頂到狀態列底部

重要:圖上圖所示,在 TranslucentStatusBar 主題下,Android 4.4 狀態列背景為預設黑色到透明的漸變,5.0+ 狀態列背景預設為半透明的黑色。

雖然實現了半透明,但是佈局被狀態列覆蓋,接下來在佈局檔案中設定fitSystemWindows(注意加到根節點ConstraintLayout上):

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.netease.mail.statusbar.DetailActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:layout_constraintTop_toTopOf="parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_collapseMode="pin"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/app_bar"
        app:layout_constraintBottom_toBottomOf="parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/text_margin"
            android:text="@string/large_text"/>

    </android.support.v4.widget.NestedScrollView>
</android.support.constraint.ConstraintLayout>
複製程式碼

來看看效果:

fitsSystemWindows 屬性加到根節點 ConstraintLayout,左邊是Android 4.4.4,右邊是 6.0

雖然佈局沒有被狀態列覆蓋,但是狀態列背景顯然這不是我們想要的效果?

為什麼狀態列會這麼奇怪?

文章開頭的定義中我們說了,佈局檔案會延伸到狀態列所佔區域下,fitsSystemWindows的作用是給對應的 View 增加 padding(這裡以 ConstraintLayout 為例),目的是為了讓其內容不被狀態列遮擋。

在我們的佈局檔案中 ConstraintLayout 沒有設定背景(預設白色),所以狀態列預設的半透明背景色和 ConstraintLayout 的白色背景疊加,就變成了上圖中的效果。

【總結】兩個基本概念:

1、windowTranslucentStatus設定為true之後,狀態列預設是半透明的(4.4 是黑色到透明色漸變,5.0+ 是純黑色半透明),和我們要求的透明相去甚遠。更重要的是,佈局會延伸到狀態列底下。

2、android:fitsSystemWindows 簡單理解就是 View 為了適配系統狀態列和導航欄(不被遮擋)自動增加 padding,當然真正的實現原理比這複雜很多而且不同的 View 可以自定義實現方式。

二、透明效果如何實現

所以,為了實現文章開頭提出來的“狀態列透明”效果,我們需要處理:

2.1 設定主題

設定 windowTranslucentStatus 為 true,讓狀態列半透明。

2.2 佈局自適應狀態列

在根節點設定 android:fitsSystemWindows 使其不被狀態列遮擋。

2.3 狀態列透明

Android 4.4 暫時沒有辦法去掉狀態列的漸變。

Android 5.0+ 開始支援修改狀態列顏色,設定透明色即可把半透明去掉。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //相容5.0及以上支援全透明
    activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
    activity.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
    activity.window.statusBarColor = Color.TRANSPARENT
}
複製程式碼

通過 theme 設定無效。

看看效果:

4.4 狀態列漸變無解,5.0+ 下狀態列透明

2.4 給佈局上色

我們看到即使狀態列透明瞭,但是其底色是一片白,因為跟節點 ConstraintLayout 沒有設定背景,大多情況下我們不會給整個跟節點設定顏色,可以考慮把 android:fitsSystemWindows 設定到子 View 上,本例中是 AppBarLayout(5.0+ 無效,只能顯式給 AppBarLayout 加 padding,可以利用其背景色),實際專案中可靈活調整。

最終效果:

最終效果

至此,完成狀態列透明效果,網上有很多庫,實際上都是基於此原理,在此基礎上再自定義 View 做為狀態列背景。

參考

https://developer.android.com/about/versions/android-4.4.html

相關文章