首先,我要實現的最終效果是這樣的,即在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的
顏色一樣。效果如下:

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

總結一下,4.4版本,需要設置兩個地方來實現沉浸式效果:
- 設置狀態欄透明
- 設置Toolbar的fitsSystemWindows屬性為true
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顏色是亮色的,再配上亮色的內容就會不明顯,上面的代碼可以解決這個問題,自動將
狀態欄的內容改為暗色調。
其他問題
-
輸入框的兼容問題
如果想要頁面底部的輸入框可以被鍵盤頂起來,并且不影響頁面的沉浸式效果,需要做兩點:
- 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);
-
其他第三方控件的兼容,如ActionMode等
有用到這個的自己去查、去嘗試吧,這個控件好久不用了,也不知道什么效果,可以參考這篇文章里的解決方法:
沉浸式狀態欄實現及遇到的坑 -
系統廠商的兼容,如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);
// }
}