Foreword
首先推廣下AndroidUtilCode,相信很多老司機(jī)們都見識(shí)過了,昨天完結(jié)了StatusBar的功能,發(fā)布了最新1.8.0版本,StatusBar相關(guān)源碼存在于BarUtils,推薦insight.io插件來查看源碼更絲滑哦,想知根知底的話那就閱讀源碼吧,后面我也會(huì)相應(yīng)介紹。
該類是借鑒于StatusBarUtil,通過我對(duì)其源碼的進(jìn)一步分析,我想到了更為完美地來解決狀態(tài)欄的疑難雜癥,也就是所謂地站在巨人肩膀上,但其中間歷經(jīng)的坎坷...欲語淚先流啊,好了,下面就讓我去前面探探路吧。
Functions
該工具類支持安卓SDK19及以上,我所設(shè)計(jì)的設(shè)置狀態(tài)欄主要包括兩類,其一是設(shè)置狀態(tài)欄顏色,其二是設(shè)置狀態(tài)欄透明度,這兩大類在應(yīng)用中主要包含六點(diǎn)。
- 設(shè)置狀態(tài)欄顏色
- 設(shè)置布局背景的狀態(tài)欄透明度
- 設(shè)置頂部View的狀態(tài)欄透明度
- 設(shè)置ViewPager中Fragment的狀態(tài)欄
- 設(shè)置滑動(dòng)返回的狀態(tài)欄
- 設(shè)置DrawLayout的狀態(tài)欄
下面我依據(jù)這六點(diǎn)來演示,效果圖的話附上API19和API25的。
設(shè)置狀態(tài)欄顏色
啥也不說了,先上效果圖。


這是相關(guān)的Demo類,可以看到,我在其中只調(diào)用了如下兩行代碼即可實(shí)現(xiàn)其效果。
private void updateStatusBar() {
BarUtils.setStatusBarColor(this, mColor, mAlpha);
BarUtils.addMarginTopEqualStatusBarHeight(mTvStatusAlpha);// 其實(shí)這個(gè)只需要調(diào)用一次即可
}
具體效果函數(shù)名已經(jīng)表達(dá)得已經(jīng)很清楚了,后面我會(huì)對(duì)其原理分析一遍,這里只看相關(guān)效果及主要代碼。
設(shè)置布局背景的狀態(tài)欄透明度


這是相關(guān)的Demo類,其主要代碼如下所示,還是兩個(gè)函數(shù)解決。
private void updateStatusBar() {
BarUtils.setStatusBarAlpha(BarStatusAlphaActivity.this, mAlpha);
BarUtils.addMarginTopEqualStatusBarHeight(mTvStatusAlpha);// 其實(shí)這個(gè)只需要調(diào)用一次即可
}
設(shè)置頂部View的狀態(tài)欄透明度


相關(guān)Demo類,由于沒有View需要添加MargionTop,所以只需一行代碼即可解決。
private void updateStatusBar() {
BarUtils.setStatusBarAlpha(BarStatusImageViewActivity.this, mAlpha, true);
}
設(shè)置ViewPager中Fragment的狀態(tài)欄


這個(gè)比較特殊,因?yàn)閂iewPager會(huì)預(yù)加載后面的Fragment,所以每一個(gè)Fragment都需要持有自己的StatusBar,這里我們?cè)O(shè)置假狀態(tài)欄即可,根據(jù)我后面的分析,你會(huì)發(fā)現(xiàn)我實(shí)現(xiàn)的狀態(tài)欄都是假的,我們?cè)诿總€(gè)需要狀態(tài)欄的Fragment中添加如下View到頂層最上方即可。
<View
android:id="@+id/fake_status_bar"
android:layout_width="match_parent"
android:layout_height="0dp" />
三個(gè)Demo類如下所示,ColorDemo類,AlphaDemo類,ImageDemo類
相關(guān)核心代碼如下所示,由于沒有需要偏移MarginTop的,一行即可解決。
public void updateFakeStatusBar() {
BarUtils.setStatusBarColor(fakeStatusBar, mColor, mAlpha);
}
public void updateFakeStatusBar() {
BarUtils.setStatusBarAlpha(fakeStatusBar, mAlpha);
}
public void updateFakeStatusBar() {
BarUtils.setStatusBarAlpha(fakeStatusBar, mAlpha);
}
設(shè)置滑動(dòng)返回的狀態(tài)欄


這是相關(guān)Demo類,由于頂部CheckBox需要偏移,其重點(diǎn)代碼只需兩行即可。
private void updateStatusBar() {
if (cbAlpha.isChecked()) {
BarUtils.setStatusBarAlpha(this, mAlpha);
} else {
BarUtils.setStatusBarColor(this, mColor, mAlpha);
}
BarUtils.addMarginTopEqualStatusBarHeight(cbAlpha);// 其實(shí)這個(gè)只需要調(diào)用一次即可
}
由于滑動(dòng)返回在API19中會(huì)出現(xiàn)桌面而不是之前的Activity界面,但這并不是我們需要關(guān)注的問題,我們需要關(guān)注的狀態(tài)欄是否也在滑動(dòng)即可,如果要求狀態(tài)欄固定不變,不跟隨滑動(dòng)的話,我們也可以做到,只需在調(diào)用函數(shù)后面加個(gè)參數(shù)true即可,也就是如下所示。
private void updateStatusBar() {
if (cbAlpha.isChecked()) {
BarUtils.setStatusBarAlpha(this, mAlpha, true);
} else {
BarUtils.setStatusBarColor(this, mColor, mAlpha, true);
}
BarUtils.addMarginTopEqualStatusBarHeight(cbAlpha);// 其實(shí)這個(gè)只需要調(diào)用一次即可
}
最后一個(gè)參數(shù)就是繪制的狀態(tài)欄是否在DecorView中,false的話就是在ContentView中,后面會(huì)具體講解,下面我們來看最后一種情況。
設(shè)置DrawLayout的狀態(tài)欄


這是相關(guān)Demo類,由于需要頂部CheckBox添加MarginTop,所以兩行代碼即可解決。
private void updateStatusBar() {
if (cbAlpha.isChecked()) {
BarUtils.setStatusBarAlpha4Drawer(BarStatusDrawerActivity.this, rootLayout, fakeStatusBar, mAlpha, cbFront.isChecked());
} else {
BarUtils.setStatusBarColor4Drawer(BarStatusDrawerActivity.this, rootLayout, fakeStatusBar, mColor, mAlpha, cbFront.isChecked());
}
BarUtils.addMarginTopEqualStatusBarHeight(cbAlpha);// 其實(shí)這個(gè)只需要調(diào)用一次即可
}
需要注意的是,DrawerLayout需要添加android:fitsSystemWindows="true"這個(gè)屬性,另外就是和Fragment一樣,需要自己在頂層最上方添加假的狀態(tài)欄。
好了,Demo已全部展示完畢,各位司機(jī)們應(yīng)該都已經(jīng)了解得差不多了,是不是如標(biāo)題所說,每種效果只需兩個(gè)函數(shù)即可終結(jié)狀態(tài)欄疑難雜癥,如果還想要深入了解源碼的話,那就繼續(xù)往下看吧,不需要了解的話那就跳過下面這節(jié)的講解,直接到最后面看我的結(jié)語吧。
How to achieve
首先我們看一張安卓UI架構(gòu)圖,如下所示。

我設(shè)計(jì)的添加顏色狀態(tài)欄或者透明狀態(tài)欄分為兩種,一種是添加在DecorView中,另一種是添加在ContentView中,我在相應(yīng)函數(shù)重載的最后一個(gè)參數(shù)boolean isDecor,就是控制是否添加到DecorView中,默認(rèn)是false,也就是添加在ContentView中的,這也是滑動(dòng)返回需要的效果,即狀態(tài)欄可隨著滑動(dòng)而偏移。
以上是其核心思想,之后的操作就是為以上服務(wù),首先我們需要把狀態(tài)欄設(shè)為透明狀態(tài),這樣我們才可以自己繪制上我們自己的狀態(tài)欄,也就是如下代碼。
private static void transparentStatusBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
window.getDecorView().setSystemUiVisibility(option);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
這段代碼即可實(shí)現(xiàn)透明狀態(tài)欄,實(shí)現(xiàn)了這一步之后就是添加我們自己假狀態(tài)欄了,首先是顏色狀態(tài)欄,如下所示,
private static void addStatusBarColor(final Activity activity, final int color, final int alpha, boolean isDecor) {
ViewGroup parent = isDecor ?
(ViewGroup) activity.getWindow().getDecorView() :
(ViewGroup) activity.findViewById(android.R.id.content);
View fakeStatusBarView = parent.findViewWithTag(TAG_COLOR);
if (fakeStatusBarView != null) {
if (fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
fakeStatusBarView.setBackgroundColor(getStatusBarColor(color, alpha));
} else {
parent.addView(createColorStatusBarView(parent.getContext(), color, alpha));
}
}
代碼很簡(jiǎn)單,根據(jù)isDecor決定添加到哪個(gè)布局,后面就是添加View的操作了。
有小伙伴對(duì)顏色狀態(tài)欄的alpha肯定有疑問,說這alpha不對(duì),并不是用來控制透明度的,的確,這個(gè)alpha并不是用來控制透明度的,這個(gè)alpha是材料設(shè)計(jì)中對(duì)狀態(tài)欄陰影設(shè)置,默認(rèn)效果值為112,下面是透明狀態(tài)欄
private static void addStatusBarAlpha(final Activity activity, final int alpha, boolean isDecor) {
ViewGroup parent = isDecor ?
(ViewGroup) activity.getWindow().getDecorView() :
(ViewGroup) activity.findViewById(android.R.id.content);
View fakeStatusBarView = parent.findViewWithTag(TAG_ALPHA);
if (fakeStatusBarView != null) {
if (fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
fakeStatusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));
} else {
parent.addView(createAlphaStatusBarView(parent.getContext(), alpha));
}
}
和顏色狀態(tài)欄很相似,我就不細(xì)說了,這個(gè)alpha,就是我們平時(shí)的透明度了。
由于我們實(shí)現(xiàn)的透明狀態(tài)欄,這樣在布局中會(huì)占滿整個(gè)屏幕,所以我這里提供了另外兩個(gè)函數(shù),分別是addMarginTopEqualStatusBarHeight、subtractMarginTopEqualStatusBarHeight,也就是增加和減少View的MarginTop為狀態(tài)欄高度,代碼如下所示。
/**
* 為view增加MarginTop為狀態(tài)欄高度
*
* @param view view
*/
public static void addMarginTopEqualStatusBarHeight(@NonNull View view) {
Object haveSetOffset = view.getTag(TAG_OFFSET);
if (haveSetOffset != null && (Boolean) haveSetOffset) return;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams.setMargins(layoutParams.leftMargin,
layoutParams.topMargin + getStatusBarHeight(),
layoutParams.rightMargin,
layoutParams.bottomMargin);
view.setTag(TAG_OFFSET, true);
}
/**
* 為view減少M(fèi)arginTop為狀態(tài)欄高度
*
* @param view view
*/
public static void subtractMarginTopEqualStatusBarHeight(@NonNull View view) {
Object haveSetOffset = view.getTag(TAG_OFFSET);
if (haveSetOffset == null || !(Boolean) haveSetOffset) return;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams.setMargins(layoutParams.leftMargin,
layoutParams.topMargin - getStatusBarHeight(),
layoutParams.rightMargin,
layoutParams.bottomMargin);
view.setTag(TAG_OFFSET, false);
}
這樣就基本滿足了我們大部分情況了,但人生總有意外,比如ViewPager的預(yù)加載,這樣我們就需要新的方式來適應(yīng)這種情況,也就是我們自己添加假的狀態(tài)欄到布局文件中,然后調(diào)用函數(shù)即可,這種方法其實(shí)就是以上兩種方法的拓展,以上兩種是我?guī)湍銈兗恿思俚脑?code>DecorView或者ContentView中,這種就是老司機(jī)們自己加到布局中,所以其本質(zhì)都是一樣,所以出來的效果也是一樣的,也就完美兼容了。
總有刁民想害朕,這不,DrawerLayout就是那個(gè)異端,為他我單獨(dú)設(shè)計(jì)了兩個(gè)函數(shù)來針對(duì)它,在Demo中我也講解了其使用方式。設(shè)計(jì)過程中需要注意的是DrawerLayout的直屬子View需要設(shè)置setFitsSystemWindows(false);,具體細(xì)節(jié)可以參看源碼。
以上基本就是設(shè)計(jì)的核心所在,老司機(jī)們想要實(shí)現(xiàn)什么樣的效果隨你們自己挑選。
具體StatusBar相關(guān)API如下所示。
getStatusBarHeight : 獲取狀態(tài)欄高度(px)
addMarginTopEqualStatusBarHeight : 為view增加MarginTop為狀態(tài)欄高度
subtractMarginTopEqualStatusBarHeight: 為view減少M(fèi)arginTop為狀態(tài)欄高度
setStatusBarColor : 設(shè)置狀態(tài)欄顏色
setStatusBarAlpha : 設(shè)置狀態(tài)欄透明度
setStatusBarColor4Drawer : 為DrawerLayout設(shè)置狀態(tài)欄顏色
setStatusBarAlpha4Drawer : 為DrawerLayout設(shè)置狀態(tài)欄透明度
Conclusion
柯基已經(jīng)為你們探完路了,實(shí)現(xiàn)狀態(tài)欄的設(shè)計(jì)沒少費(fèi)我功夫,想實(shí)現(xiàn)什么效果,最終只需兩個(gè)函數(shù)即可藥到病除,我們程序員不就是需要這種越簡(jiǎn)單越好的東西么,同意的話就快砸上你們的喜歡吧。