徹底搞定Android Kitkat+沉浸式狀態欄效果

首先,我要實現的最終效果是這樣的,即在Android4.4及以上版本系統上,統一顯示為如下效果:


最終效果
最終效果

所謂“沉浸式”狀態欄

這里所說的沉浸式狀態欄,就是指上面的效果,狀態欄和Toolbar的顏色保持一致,融為一體的效果。

版本差異及解決方法

本文所用的示例使用的style風格是NoActionBar的,標題欄使用的是Toolbar控件,請知悉。

Android4.4

Android4.4以前的版本,狀態欄的顏色都是黑色的,而且無法修改;但一般APP的Toolbar都不會設置為黑色,于是,兩者
有十分明顯的顏色區分,各自占有不同的區域,填充不同的顏色。簡單的說,Android4.4以前的版本是無法做到
沉浸式的效果的(做系統開發的除外),所以如果想要統一風格的話,可以設置APP最小支持的版本為4.4,如果
不行,就沒辦法了,只能4.4以前一個樣式,4.4及以后一個樣式。

Android4.4開始,新增了設置狀態欄背景色透明的屬性,新增的屬性是這兩個:

WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS 
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION

這兩個屬性分別設置狀態欄和導航欄背景色為全透明。(這里只討論狀態欄,只設置上面一個屬性就行,導航欄的類似)
實現方法很簡單,在Activity初始化時,調用以下代碼:

// Translucent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    Window window = getWindow();
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}

由于是代碼設置,所以無法通過xml預覽到想要的結果,一定要在4.4真機上運行才能看到效果。

到這里可能存在兩個問題:

1. Toolbar把狀態欄的空間占用了,擠到一起。

2. 狀態欄和Toolbar并沒有完全融為一體,而是從上到下,由黑色漸變到Toolbar的顏色。

第一個現象是必現的,解決方法是在Toolbar的布局文件里加上一個屬性:

android:fitsSystemWindows="true"

這個屬性必須加在Toolbar或者Toolbar的父控件上,也就是,如果Toolbar直接寫在Activity的布局文件里,則在Toolbar上
加這個屬性,如果Toolbar是include到Activity的布局文件里,則可以加到Toolbar的父控件里;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context="com.chengsy.immersive.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:fitsSystemWindows="true"
        app:title="@string/app_name"
        app:titleTextColor="#FFFFFF">

    </android.support.v7.widget.Toolbar>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello World!" />

</LinearLayout>

上面這種情況,如果寫到linearlayout中,就會出現問題,StatusBar的背景色跟整個窗口的背景色一樣(或者跟桌面顏色一樣),而不是和Toolbar的
顏色一樣。效果如下:

屬性設置錯誤效果
屬性設置錯誤效果

第二個現象根據不同的系統廠商,效果不一樣,也就是不同的手機廠商做的不一樣,沒辦法,但是不是太影響沉浸式的效果。
效果如下:

漸變的StatusBar
漸變的StatusBar

總結一下,4.4版本,需要設置兩個地方來實現沉浸式效果:

  1. 設置狀態欄透明
  2. 設置Toolbar的fitsSystemWindows屬性為true

Android5.0

先看看通過上面的設置,程序運行在Android5.0的設備上是什么效果:
可能的效果也有兩個,一個是我們想要的效果,沉浸式,不再配圖了,另一個是這樣的:

在Android5.0部分機器上的效果
在Android5.0部分機器上的效果

這種效果是狀態欄的顏色上面覆蓋了一層半透明的顏色,不是全透明。當然,原因你懂得,不同廠商系統設計師的idea是不一樣的,
但是這里說一下,谷歌官方的Mertial Design的設計規范是如上圖所示,并不是沉浸式的效果,但國內的APP現在普遍比較
喜歡沉浸式的效果。

那么如果要統一5.0的效果跟4.4的效果保持一致,全部都是沉浸式該怎么辦???
這么辦---
首先,Android5.0開始,系統又新增了設置狀態欄顏色值的屬性和接口:

WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS

通過這個屬性來設置設置狀態欄的背景色,來實現兩者融為一體的效果,代碼實現如下:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        getWindow().setStatusBarColor(Color.TRANSPARENT);// SDK21
}

原理很簡單,首先要清空之前設置的FLAG_TRANSLUCENT_STATUS屬性,然后添加修改狀態欄背景色的屬性FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
,最后給狀態欄設置為全透明。

這里有坑,有的5.x的設備,如果調用這句代碼
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
狀態欄會變黑色,這時候需要去掉這句。但是去掉這句代碼,在6.0+系統上半透明的狀態欄又出現了。所以處理方法是判斷如果是某種特殊的系統,
不調用上述代碼。

Android6.0

對于Android6.0及以后的系統,對這方面的支持就已經很完善和統一了,通過4.4設置的FLAG_TRANSLUCENT_STATUS屬性和
fitsSystemWindows屬性就可以達到想要的效果了,不需要做特殊處理,版本內暫時不存在機型之間的差異。

但是6.0也不是完全沒新的東西(指的是狀態欄這一塊內容),6.0新增了設置狀態欄里內容色調的屬性和接口,通過設置如下屬性,可以把狀態欄的文字
色調由亮色改為暗色,亮色是白色,暗色是黑色:

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

看名字感覺跟我的描素不一樣啊,其實,這個屬性是把狀態欄標記為亮色調,從而,系統會自動把狀態欄的文字內容變為
暗色調,如果你的APP的Toolbar顏色是亮色的,再配上亮色的內容就會不明顯,上面的代碼可以解決這個問題,自動將
狀態欄的內容改為暗色調。

其他問題

  1. 輸入框的兼容問題

    如果想要頁面底部的輸入框可以被鍵盤頂起來,并且不影響頁面的沉浸式效果,需要做兩點:

     - AndroidManifest.xml文件配置鍵盤屬性:android:windowSoftInputMode="adjustResize"
     - Activity頁面根布局設置 android:fitsSystemWindows="true" 屬性
    

    布局代碼如下:

     <?xml version="1.0" encoding="utf-8"?>
     <RelativeLayout 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"
         android:orientation="vertical"
         tools:context="com.chengsy.immersive.MainActivity">
     
         <android.support.v7.widget.Toolbar
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:background="@color/colorPrimary"
             android:fitsSystemWindows="true"
             app:title="@string/app_name"
             app:titleTextColor="#FFFFFF">
     
         </android.support.v7.widget.Toolbar>
     
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_below="@+id/toolbar"
             android:gravity="bottom">
     
             <EditText
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:text="Hello World!" />
         </LinearLayout>
     
     </RelativeLayout>
    

    通過上面的處理,狀態欄的顏色不對了,變成了白色或者桌面的背景色,問題又來了。解決方法的原理就是給狀態欄填充上顏色,
    但是,給狀態欄修改顏色的屬性和接口在5.0才出現,[4.4--5.0)版本的怎么辦?用SystemBarTintManager,Github上的開源庫,
    解決眼前的問題。使用很簡單,引用這個庫:

     compile 'com.readystatesoftware.systembartint:systembartint:1.0.3'
    

    代碼中設置:

     private SystemBarTintManager tintManager;
     
     tintManager = new SystemBarTintManager(this);
     tintManager.setStatusBarTintColor(getResources().getColor(R.color.colorPrimary));
     tintManager.setStatusBarTintEnabled(true);
    
  2. 其他第三方控件的兼容,如ActionMode等

    有用到這個的自己去查、去嘗試吧,這個控件好久不用了,也不知道什么效果,可以參考這篇文章里的解決方法:
    沉浸式狀態欄實現及遇到的坑

  3. 系統廠商的兼容,如MIUI系統等

    這里也介紹了MIUI系統的適配問題,供參考:
    沉浸式狀態欄實現及遇到的坑

總結

由于Android系統的開放性,以及系統廠商的不統一,導致Android系統的適配成為了另程序員頭疼的一大問題,沉浸式的效果同樣
不好做到完全統一樣式,只能盡力而為之。通過上面的操作,基本可以保證大多數機型和系統的沉浸式效果,但仍然有個別無法適配的
系統或機型,這里也無法一一列舉所有的情形,需要程序員們有針對性的設計代碼,解決問題。

這里給出自己總結的一個方法,可以在BaseActivity中調用:

首先,Toolbar要設置fitsSystemWindows屬性,如果頁面包含EditText,需同時在頁面根布局添加fitsSystemWindows屬性;
其次,引用SystemBarTintManager庫提供支持;
最后,調用如下代碼:

private SystemBarTintManager tintManager;
protected void initWindow() {
    // 4.4及以上版本設置狀態欄透明
    Window window = getWindow();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        // Translucent status bar
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }

    // 解決4.4-5.0版本之間,頁面包含EditText無法適配的問題
    {
        // create our manager instance after the content view is set
        mTintManager = new SystemBarTintManager(this);
        // enable status bar tint
        mTintManager.setStatusBarTintEnabled(true);
        // enable navigation bar tint
        mTintManager.setNavigationBarTintEnabled(true);

        // 自定義狀態欄的顏色
        mTintManager.setStatusBarTintColor(getResources().getColor(R.color.colorPrimary));
    }

    // 解決[5.0-5.1.1]版本狀態欄沒有全透明的系統適配問題
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // 解決部分5.x系統使用狀態欄透明屬性后狀態欄變黑色,不使用這句代碼,在6.0設備上又出現半透明狀態欄
        // 需要特殊處理
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);
    }
    
    // 把狀態欄標記為淺色,然后狀態欄的字體顏色自動轉換為深色。
    // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    //     getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    // }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,582評論 25 707
  • 前言 原文:http://blog.csdn.net/mybeta/article/details/5076032...
    naturs閱讀 23,136評論 8 70
  • 背景 上篇文章一個千萬量級的APP使用的一些第三方庫中,在說到一個使用很廣泛的滑動退出庫SwipeBackLayo...
    Ziv_xiao閱讀 16,693評論 22 127
  • 前言 首先請大家看幾張圖: 以上的效果,一般我們統稱為沉浸式狀態欄。其實,這種叫法不是很準確,而且也沒有沉浸式狀態...
    宇是我閱讀 3,900評論 2 28
  • 交朋友這件小事,很像狗熊掰玉米。 從幼時到現在,掰一路扔一路。 末了駐足一看——咦,沒撈著幾個嘛?! 幼時和同院的...
    石長生之閱讀 800評論 3 2