** 本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布*
狀態(tài)欄著色,也就是我們經(jīng)常聽到的沉浸式狀態(tài)欄,關(guān)于沉浸式的稱呼網(wǎng)上也有很多吐槽的,這里就不做過(guò)多討論了,以下我們統(tǒng)稱狀態(tài)欄著色,這樣我覺得更加容易理解。
從Android4.4開始,才可以實(shí)現(xiàn)狀態(tài)欄著色,并且從5.0開始系統(tǒng)更加完善了這一功能,可直接在主題中設(shè)置<item name="colorPrimaryDark">@color/colorPrimaryDark</item>或者getWindow().setStatusBarColor(color)來(lái)實(shí)現(xiàn),但畢竟4.4+的機(jī)器還有很大的占比,所以就有必要尋求其它的解決方案。
第一種方案:
1、首先將手機(jī)手機(jī)狀態(tài)欄透明化:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0及以上
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
getWindow().setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4到5.0
WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
}
在相應(yīng)的Activity或基類執(zhí)行這段代碼就ok了。
可見在4.4到5.0的系統(tǒng)、5.0及以上系統(tǒng)的處理方式有所不同,除了這種代碼修改額方式外,還可以通過(guò)主題來(lái)修改,需要在values、values-v19、values-v21目錄下分別創(chuàng)建相應(yīng)的主題:
//values
<style name="TranslucentTheme" parent="AppTheme">
</style>
//values-v19
<style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">false</item>
</style>
//values-v21
<style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
給相應(yīng)Activity或Application設(shè)置該主題就ok了。
兩種方式根據(jù)需求選擇就好了,到這里我們就完成了第一步,將狀態(tài)欄透明化了。
2、給狀態(tài)欄著色
完成了第一步,我們開始給狀態(tài)欄加上想要的色彩吧!
將狀態(tài)欄透明化后,直接運(yùn)行項(xiàng)目會(huì)發(fā)現(xiàn)界面布局直接延伸到狀態(tài)欄,針對(duì)這種情況首先可以在布局文件使用android:fitsSystemWindows="true"屬性讓布局不延伸到狀態(tài)欄,接下來(lái)添加一個(gè)和狀態(tài)欄高、寬相同的指定顏色View來(lái)覆蓋被透明化的狀態(tài)欄,或者使布局的背景顏色為需要的狀態(tài)欄顏色。這兩種方式在后邊會(huì)說(shuō)到,這里我們先看另外一種方式,也是個(gè)人比較喜歡的。我們分一下幾種場(chǎng)景來(lái)討論:
先做一些準(zhǔn)備工作,在values、values-v19目錄添加如下尺寸:
//values
<dimen name="padding_top">0dp</dimen>
//values-v19
<dimen name="padding_top">25dp</dimen>
關(guān)于25dp,在有些系統(tǒng)上可能有誤差,這里不做討論!
2.1 頁(yè)面頂部使用Toolbar(或自定義title)
一般情況狀態(tài)欄的顏色和Toolbar的顏色相同,既然狀態(tài)欄透明化后,布局頁(yè)面延伸到了狀態(tài)欄,何不給Toolbar加上一個(gè)狀態(tài)欄高度的頂部padding呢:
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:paddingTop="@dimen/padding_top"
android:theme="@style/AppTheme.AppBarOverlay" />
效果如下:

至于自定義title的情況經(jīng)測(cè)試也ok的,圖就不貼了,有興趣可看代碼。
2.2、 DrawerLayout + NavigationView + Toolbar
同樣先給Toolbar設(shè)置頂部padding,在4.4系統(tǒng)上看下效果:

NavigationView竟然沒延伸到狀態(tài)欄,好吧,繼續(xù)修改,當(dāng)系統(tǒng)版本小于5.0時(shí),進(jìn)行如下設(shè)置:
private void navViewToTop() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
mDrawerLayout.setFitsSystemWindows(true);
mDrawerLayout.setClipToPadding(false);
}
}
再看效果:

2.3、頁(yè)面頂部是一張圖片
這種其實(shí)是最簡(jiǎn)單的,因?yàn)闋顟B(tài)欄透明化后,布局已經(jīng)延伸到狀態(tài),所以不需要其它額外操作:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@mipmap/top_image" />
</LinearLayout>
效果如下:

2.4、頁(yè)面底部切換Tab + fragment
某些情況下,當(dāng)我們的頁(yè)面采用底部切換Tab + 多個(gè)fragment時(shí),可能每個(gè)fragment的頂部顏色不一樣。如何實(shí)現(xiàn)狀態(tài)欄顏色跟隨fragment切換來(lái)變化呢?其實(shí)原理和2.1類似,這里我們每個(gè)fragment采用自定義的Title,先看FragmentOne的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff9900"
android:paddingTop="@dimen/padding_top">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="fragment one" />
</LinearLayout>
</LinearLayout>
我們?cè)O(shè)置Title的背景為橙黃色、同時(shí)設(shè)置paddingTop,F(xiàn)ragmentTwo的布局類似,只是Title的背景為藍(lán)色。然后將兩個(gè)Fragment添加到Activity中,最后看下切換的效果:

嗯,效果還是不錯(cuò)的,沒有延遲的情況!可惜沒早點(diǎn)發(fā)掘這種方式,以前的方案效果略不理想。
第二種方案:
在方案一中,我們沒有使用android:fitsSystemWindows="true"屬性,而是將布局延伸到狀態(tài)欄來(lái)處理,這次我們使用android:fitsSystemWindows="true"屬性,不讓布局延伸到狀態(tài)欄,這時(shí)狀態(tài)欄就是透明的,然后添加一個(gè)和狀態(tài)欄高、寬相同的指定顏色View來(lái)覆蓋被透明化的狀態(tài)欄。我們一步步來(lái)實(shí)現(xiàn)。
1、第一步還是先將狀態(tài)欄透明化,方法同上。
2、在布局文件中添加android:fitsSystemWindows="true"屬性:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/AppTheme.AppBarOverlay"
app:title="第二種方案" />
</LinearLayout>
3、創(chuàng)建View并添加到狀態(tài)欄:
private void addStatusBarView() {
View view = new View(this);
view.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(this));
ViewGroup decorView = (ViewGroup) findViewById(android.R.id.content);
decorView.addView(view, params);
}
原理很簡(jiǎn)單,但是要額外寫這些代碼。。。最后看下效果:

第三種方案:
和方案二類似,同樣使用android:fitsSystemWindows="true"屬性,再修改布局文件的根布局為需要的狀態(tài)欄顏色,因根布局的顏色被修改,所以你需要在里邊多嵌套一層布局,來(lái)指定界面的主背景色,比如白色等等,否則就和狀態(tài)欄顏色一樣了。說(shuō)起來(lái)有點(diǎn)抽象,還是看具體的例子吧:
1、先將狀態(tài)欄透明化,方法同上。
2、修改布局文件:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff9900"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff9900"
android:theme="@style/AppTheme.AppBarOverlay"
app:title="第三種方案" />
</LinearLayout>
</RelativeLayout>
修改完了,看效果:

如果項(xiàng)目有幾十個(gè)界面,這樣的方式修改起來(lái)還是挺累的,你還要考慮各種嵌套問(wèn)題。
后兩種方案的例子相對(duì)簡(jiǎn)單,有興趣的話你可以嘗試更多的場(chǎng)景!
三種方式如何選擇,相信到這里你應(yīng)該有答案了吧,我個(gè)人更喜歡第一種!
如果你有更好的方案,歡迎指點(diǎn)哦!