只需兩個(gè)函數(shù)終結(jié)狀態(tài)欄疑難雜癥

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)。

  1. 設(shè)置狀態(tài)欄顏色
  2. 設(shè)置布局背景的狀態(tài)欄透明度
  3. 設(shè)置頂部View的狀態(tài)欄透明度
  4. 設(shè)置ViewPager中Fragment的狀態(tài)欄
  5. 設(shè)置滑動(dòng)返回的狀態(tài)欄
  6. 設(shè)置DrawLayout的狀態(tài)欄

下面我依據(jù)這六點(diǎn)來演示,效果圖的話附上API19和API25的。

設(shè)置狀態(tài)欄顏色

啥也不說了,先上效果圖。

color19.gif
color25.gif

這是相關(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)欄透明度

bg19.gif
bg25.gif

這是相關(guān)的Demo類,其主要代碼如下所示,還是兩個(gè)函數(shù)解決。

private void updateStatusBar() {
    BarUtils.setStatusBarAlpha(BarStatusAlphaActivity.this, mAlpha);
    BarUtils.addMarginTopEqualStatusBarHeight(mTvStatusAlpha);// 其實(shí)這個(gè)只需要調(diào)用一次即可
}

設(shè)置頂部View的狀態(tài)欄透明度

image19.gif
image25.gif

相關(guān)Demo類,由于沒有View需要添加MargionTop,所以只需一行代碼即可解決。

private void updateStatusBar() {
    BarUtils.setStatusBarAlpha(BarStatusImageViewActivity.this, mAlpha, true);
}

設(shè)置ViewPager中Fragment的狀態(tài)欄

fragment19.gif
fragment25.gif

這個(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)欄

back19.gif
back25.gif

這是相關(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)欄

drawer19.gif
drawer25.gif

這是相關(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)圖,如下所示。

ui_frame.png

我設(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)單越好的東西么,同意的話就快砸上你們的喜歡吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容