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

所謂“沉浸式”狀態(tài)欄
這里所說的沉浸式狀態(tài)欄,就是指上面的效果,狀態(tài)欄和Toolbar的顏色保持一致,融為一體的效果。
版本差異及解決方法
本文所用的示例使用的style風(fēng)格是NoActionBar的,標(biāo)題欄使用的是Toolbar控件,請知悉。
Android4.4
Android4.4以前的版本,狀態(tài)欄的顏色都是黑色的,而且無法修改;但一般APP的Toolbar都不會設(shè)置為黑色,于是,兩者
有十分明顯的顏色區(qū)分,各自占有不同的區(qū)域,填充不同的顏色。簡單的說,Android4.4以前的版本是無法做到
沉浸式的效果的(做系統(tǒng)開發(fā)的除外),所以如果想要統(tǒng)一風(fēng)格的話,可以設(shè)置APP最小支持的版本為4.4,如果
不行,就沒辦法了,只能4.4以前一個樣式,4.4及以后一個樣式。
Android4.4開始,新增了設(shè)置狀態(tài)欄背景色透明的屬性,新增的屬性是這兩個:
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
這兩個屬性分別設(shè)置狀態(tài)欄和導(dǎo)航欄背景色為全透明。(這里只討論狀態(tài)欄,只設(shè)置上面一個屬性就行,導(dǎo)航欄的類似)
實現(xiàn)方法很簡單,在Activity初始化時,調(diào)用以下代碼:
// Translucent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
由于是代碼設(shè)置,所以無法通過xml預(yù)覽到想要的結(jié)果,一定要在4.4真機上運行才能看到效果。
到這里可能存在兩個問題:
1. Toolbar把狀態(tài)欄的空間占用了,擠到一起。
2. 狀態(tài)欄和Toolbar并沒有完全融為一體,而是從上到下,由黑色漸變到Toolbar的顏色。
第一個現(xiàn)象是必現(xiàn)的,解決方法是在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中,就會出現(xiàn)問題,StatusBar的背景色跟整個窗口的背景色一樣(或者跟桌面顏色一樣),而不是和Toolbar的
顏色一樣。效果如下:

第二個現(xiàn)象根據(jù)不同的系統(tǒng)廠商,效果不一樣,也就是不同的手機廠商做的不一樣,沒辦法,但是不是太影響沉浸式的效果。
效果如下:

總結(jié)一下,4.4版本,需要設(shè)置兩個地方來實現(xiàn)沉浸式效果:
- 設(shè)置狀態(tài)欄透明
- 設(shè)置Toolbar的fitsSystemWindows屬性為true
Android5.0
先看看通過上面的設(shè)置,程序運行在Android5.0的設(shè)備上是什么效果:
可能的效果也有兩個,一個是我們想要的效果,沉浸式,不再配圖了,另一個是這樣的:

這種效果是狀態(tài)欄的顏色上面覆蓋了一層半透明的顏色,不是全透明。當(dāng)然,原因你懂得,不同廠商系統(tǒng)設(shè)計師的idea是不一樣的,
但是這里說一下,谷歌官方的Mertial Design的設(shè)計規(guī)范是如上圖所示,并不是沉浸式的效果,但國內(nèi)的APP現(xiàn)在普遍比較
喜歡沉浸式的效果。
那么如果要統(tǒng)一5.0的效果跟4.4的效果保持一致,全部都是沉浸式該怎么辦???
這么辦---
首先,Android5.0開始,系統(tǒng)又新增了設(shè)置狀態(tài)欄顏色值的屬性和接口:
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
通過這個屬性來設(shè)置設(shè)置狀態(tài)欄的背景色,來實現(xiàn)兩者融為一體的效果,代碼實現(xiàn)如下:
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
}
原理很簡單,首先要清空之前設(shè)置的FLAG_TRANSLUCENT_STATUS屬性,然后添加修改狀態(tài)欄背景色的屬性FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
,最后給狀態(tài)欄設(shè)置為全透明。
這里有坑,有的5.x的設(shè)備,如果調(diào)用這句代碼
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
狀態(tài)欄會變黑色,這時候需要去掉這句。但是去掉這句代碼,在6.0+系統(tǒng)上半透明的狀態(tài)欄又出現(xiàn)了。所以處理方法是判斷如果是某種特殊的系統(tǒng),
不調(diào)用上述代碼。
Android6.0
對于Android6.0及以后的系統(tǒng),對這方面的支持就已經(jīng)很完善和統(tǒng)一了,通過4.4設(shè)置的FLAG_TRANSLUCENT_STATUS屬性和
fitsSystemWindows屬性就可以達到想要的效果了,不需要做特殊處理,版本內(nèi)暫時不存在機型之間的差異。
但是6.0也不是完全沒新的東西(指的是狀態(tài)欄這一塊內(nèi)容),6.0新增了設(shè)置狀態(tài)欄里內(nèi)容色調(diào)的屬性和接口,通過設(shè)置如下屬性,可以把狀態(tài)欄的文字
色調(diào)由亮色改為暗色,亮色是白色,暗色是黑色:
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
看名字感覺跟我的描素不一樣啊,其實,這個屬性是把狀態(tài)欄標(biāo)記為亮色調(diào),從而,系統(tǒng)會自動把狀態(tài)欄的文字內(nèi)容變?yōu)?br>
暗色調(diào),如果你的APP的Toolbar顏色是亮色的,再配上亮色的內(nèi)容就會不明顯,上面的代碼可以解決這個問題,自動將
狀態(tài)欄的內(nèi)容改為暗色調(diào)。
其他問題
-
輸入框的兼容問題
如果想要頁面底部的輸入框可以被鍵盤頂起來,并且不影響頁面的沉浸式效果,需要做兩點:
- AndroidManifest.xml文件配置鍵盤屬性:android:windowSoftInputMode="adjustResize" - Activity頁面根布局設(shè)置 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>通過上面的處理,狀態(tài)欄的顏色不對了,變成了白色或者桌面的背景色,問題又來了。解決方法的原理就是給狀態(tài)欄填充上顏色,
但是,給狀態(tài)欄修改顏色的屬性和接口在5.0才出現(xiàn),[4.4--5.0)版本的怎么辦?用SystemBarTintManager,Github上的開源庫,
解決眼前的問題。使用很簡單,引用這個庫:compile 'com.readystatesoftware.systembartint:systembartint:1.0.3'代碼中設(shè)置:
private SystemBarTintManager tintManager; tintManager = new SystemBarTintManager(this); tintManager.setStatusBarTintColor(getResources().getColor(R.color.colorPrimary)); tintManager.setStatusBarTintEnabled(true); -
其他第三方控件的兼容,如ActionMode等
有用到這個的自己去查、去嘗試吧,這個控件好久不用了,也不知道什么效果,可以參考這篇文章里的解決方法:
沉浸式狀態(tài)欄實現(xiàn)及遇到的坑 -
系統(tǒng)廠商的兼容,如MIUI系統(tǒng)等
這里也介紹了MIUI系統(tǒng)的適配問題,供參考:
沉浸式狀態(tài)欄實現(xiàn)及遇到的坑
總結(jié)
由于Android系統(tǒng)的開放性,以及系統(tǒng)廠商的不統(tǒng)一,導(dǎo)致Android系統(tǒng)的適配成為了另程序員頭疼的一大問題,沉浸式的效果同樣
不好做到完全統(tǒng)一樣式,只能盡力而為之。通過上面的操作,基本可以保證大多數(shù)機型和系統(tǒng)的沉浸式效果,但仍然有個別無法適配的
系統(tǒng)或機型,這里也無法一一列舉所有的情形,需要程序員們有針對性的設(shè)計代碼,解決問題。
這里給出自己總結(jié)的一個方法,可以在BaseActivity中調(diào)用:
首先,Toolbar要設(shè)置fitsSystemWindows屬性,如果頁面包含EditText,需同時在頁面根布局添加fitsSystemWindows屬性;
其次,引用SystemBarTintManager庫提供支持;
最后,調(diào)用如下代碼:
private SystemBarTintManager tintManager;
protected void initWindow() {
// 4.4及以上版本設(shè)置狀態(tài)欄透明
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);
// 自定義狀態(tài)欄的顏色
mTintManager.setStatusBarTintColor(getResources().getColor(R.color.colorPrimary));
}
// 解決[5.0-5.1.1]版本狀態(tài)欄沒有全透明的系統(tǒng)適配問題
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 解決部分5.x系統(tǒng)使用狀態(tài)欄透明屬性后狀態(tài)欄變黑色,不使用這句代碼,在6.0設(shè)備上又出現(xiàn)半透明狀態(tài)欄
// 需要特殊處理
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);
}
// 把狀態(tài)欄標(biāo)記為淺色,然后狀態(tài)欄的字體顏色自動轉(zhuǎn)換為深色。
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
// }
}