還用第三方庫管理狀態欄嗎?Android關於狀態欄管理的幾種方案實現!

語言: CN / TW / HK

theme: juejin highlight: a11y-dark


我正在參與掘金創作者訓練營第6期,點擊瞭解活動詳情

前言

在我們開發應用的過程中,關於狀態欄的管理是不得不提的事情,一般我們通過開源的一些第三方庫來管理,並沒有瞭解它實現原理。

image.png

狀態欄説的就是我們頂部的那個大黑邊了,一個應用的狀態欄,我們一般常用的幾種操作如下:

  1. 設置狀態欄的背景顏色(如果是6.0以下需要兼容處理白色背景)
  2. 設置狀態欄的背景圖片
  3. 設置狀態欄開啟沉浸式和關閉沉浸式
  4. 設置狀態欄文本圖標顏色(黑色與白色)
  5. 設置指定佈局適配狀態欄高度

一般來説操作狀態欄有兩種思路,一種是直接操作系統狀態欄,一般使用一個工具類來實現。另一種是棄用系統狀態,使用宿主自定義View的方案代替 DecorView 中的真正佈局,間距的操作'狀態欄'。這裏的狀態欄打引號,因為這個狀態欄是我們自定義View實現的。

下面我們來看看他們分別如何實現與操作。

一、系統原生狀態欄

由於Android的狀態欄處理不同的系統版本處理的方式不同,這裏只兼容到4.4版本以上,也是我們常用的最低版本。

1.1 狀態欄管理工具類

一般我們通過狀態欄的工具類添加一些Flag,操作管理系統的狀態欄。

```java /* * 狀態欄透明,狀態欄黑色文字,狀態欄顏色,沉浸式狀態欄 / public class StatusBarUtils {

public static int DEFAULT_COLOR = 0;
public static float DEFAULT_ALPHA = 0;

/**
 * 設置狀態欄背景顏色
 */
public static void setColor(Activity activity, @ColorInt int color) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        activity.getWindow().setStatusBarColor(color);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        ViewGroup systemContent = activity.findViewById(android.R.id.content);
        View statusBarView = new View(activity);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
        statusBarView.setBackgroundColor(color);
        systemContent.getChildAt(0).setFitsSystemWindows(true);
        systemContent.addView(statusBarView, 0, lp);
    }
}


public static void immersive(Activity activity) {
    immersive(activity, DEFAULT_COLOR, DEFAULT_ALPHA);
}

public static void immersive(Activity activity, int color, @FloatRange(from = 0.0, to = 1.0) float alpha) {
    immersive(activity.getWindow(), color, alpha);
}

public static void immersive(Activity activity, int color) {
    immersive(activity.getWindow(), color, 1f);
}

public static void immersive(Window window) {
    immersive(window, DEFAULT_COLOR, DEFAULT_ALPHA);
}

public static void immersive(Window window, int color) {
    immersive(window, color, 1f);
}

public static void immersive(Window window, int color, @FloatRange(from = 0.0, to = 1.0) float alpha) {
    if (Build.VERSION.SDK_INT >= 21) {
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(mixtureColor(color, alpha));

        int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
        systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        window.getDecorView().setSystemUiVisibility(systemUiVisibility);
    } else if (Build.VERSION.SDK_INT >= 19) {
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        setTranslucentView((ViewGroup) window.getDecorView(), color, alpha);
    } else if (Build.VERSION.SDK_INT > 16) {
        int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
        systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
        systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        window.getDecorView().setSystemUiVisibility(systemUiVisibility);
    }
}

/**
 * 創建假的透明欄
 */
public static void setTranslucentView(ViewGroup container, int color, @FloatRange(from = 0.0, to = 1.0) float alpha) {
    if (Build.VERSION.SDK_INT >= 19) {
        int mixtureColor = mixtureColor(color, alpha);
        View translucentView = container.findViewById(android.R.id.custom);
        if (translucentView == null && mixtureColor != 0) {
            translucentView = new View(container.getContext());
            translucentView.setId(android.R.id.custom);
            ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(container.getContext()));
            container.addView(translucentView, lp);
        }
        if (translucentView != null) {
            translucentView.setBackgroundColor(mixtureColor);
        }
    }
}

public static int mixtureColor(int color, @FloatRange(from = 0.0, to = 1.0) float alpha) {
    int a = (color & 0xff000000) == 0 ? 0xff : color >>> 24;
    return (color & 0x00ffffff) | (((int) (a * alpha)) << 24);
}

// ========================  狀態欄字體顏色設置  ↓ ================================


/**
 * 設置狀態欄黑色字體圖標
 */
public static boolean setStatusBarBlackText(Activity activity) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Window window = activity.getWindow();
        View decorView = window.getDecorView();

        decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

        return true;
    }
    return false;
}

/**
 * 設置狀態欄白色字體圖標
 */
public static boolean setStatusBarWhiteText(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Window window = activity.getWindow();
        View decorView = window.getDecorView();

        decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

        return true;
    }
    return false;
}

// ========================  狀態欄字體顏色設置  ↑================================


// 在某些機子上存在不同的density值,所以增加兩個虛擬值
private static int sStatusBarHeight = -1;
private static float sVirtualDensity = -1;
private final static int STATUS_BAR_DEFAULT_HEIGHT_DP = 25; // 大部分狀態欄都是25dp

/**
 * 獲取狀態欄的高度。
 */
public static int getStatusBarHeight(Context context) {
    if (sStatusBarHeight == -1) {
        initStatusBarHeight(context);
    }
    return sStatusBarHeight;
}

private static void initStatusBarHeight(Context context) {
    Class<?> clazz;
    Object obj = null;
    Field field = null;
    try {
        clazz = Class.forName("com.android.internal.R$dimen");
        obj = clazz.newInstance();
        if (DeviceUtils.isMeizu()) {
            try {
                field = clazz.getField("status_bar_height_large");
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        if (field == null) {
            field = clazz.getField("status_bar_height");
        }
    } catch (Throwable t) {
        t.printStackTrace();
    }
    if (field != null && obj != null) {
        try {
            int id = Integer.parseInt(field.get(obj).toString());
            sStatusBarHeight = context.getResources().getDimensionPixelSize(id);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
    if (DeviceUtils.isTablet(context)
            && sStatusBarHeight > CommUtils.dip2px(STATUS_BAR_DEFAULT_HEIGHT_DP)) {
        //狀態欄高度大於25dp的平板,狀態欄通常在下方
        sStatusBarHeight = 0;
    } else {
        if (sStatusBarHeight <= 0) {
            if (sVirtualDensity == -1) {
                sStatusBarHeight = CommUtils.dip2px(STATUS_BAR_DEFAULT_HEIGHT_DP);
            } else {
                sStatusBarHeight = (int) (STATUS_BAR_DEFAULT_HEIGHT_DP * sVirtualDensity + 0.5f);
            }
        }
    }
}


// ========================  適配狀態欄高度  ↓ ================================

/**
 * 適配狀態欄高度的View - 設置Padding
 */
public static void fitsStatusBarViewPadding(View view) {
    //增加高度
    ViewGroup.LayoutParams lp = view.getLayoutParams();
    lp.height += getStatusBarHeight(view.getContext());

    //設置PaddingTop
    view.setPadding(view.getPaddingLeft(),
            view.getPaddingTop() + getStatusBarHeight(view.getContext()),
            view.getPaddingRight(),
            view.getPaddingBottom());
}

/**
 * 適配狀態欄高度的View - 設置Margin
 */
public static void fitsStatusBarViewMargin(View view) {

    if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {

        //已經添加過了不要再次設置
        if (view.getTag() != null && view.getTag().equals("fitStatusBar")) {
            return;
        }

        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        int marginTop = layoutParams.topMargin;
        int setMarginTop = marginTop + getStatusBarHeight(view.getContext());
        view.setTag("fitStatusBar");
        layoutParams.topMargin = setMarginTop;
        view.requestLayout();
    }
}

/**
 * 適配狀態欄高度的View - 使用佈局包裹
 */
public static void fitsStatusBarViewLayout(View view) {

    ViewParent fitParent = view.getParent();
    if (fitParent != null) {

        if (((fitParent instanceof LinearLayout) &&
                ((ViewGroup) fitParent).getTag() != null &&
                ((ViewGroup) fitParent).getTag().equals("fitLayout"))) {
            //已經添加過了不要再次設置
            return;
        }

        //給當前佈局包裝一個適應佈局
        ViewGroup fitGroup = (ViewGroup) fitParent;
        fitGroup.removeView(view);

        LinearLayout fitLayout = new LinearLayout(view.getContext());
        fitLayout.setOrientation(LinearLayout.VERTICAL);
        fitLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        fitLayout.setTag("fitLayout");

        //先加一個狀態欄高度的佈局
        View statusView = new View(view.getContext());
        statusView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(view.getContext())));
        fitLayout.addView(statusView);

        ViewGroup.LayoutParams fitViewParams = view.getLayoutParams();
        fitLayout.addView(view);

        fitGroup.addView(fitLayout);
    }
}

}

```

1.2 系統狀態欄的背景和文本顏色處理

通過工具類操作的形式,我們一般封裝到BaseActivity中,比如定義一些方法:

```kotlin /* * 設置頂部狀態欄的顏色(默認為白色背景-黑色文字) / protected fun setStatusBarColor(): Int { //如果狀態欄文字能變黑那麼背景設置為白色,否則返回背景灰色文本默認為白色 return if (StatusBarUtils.setStatusBarBlackText(this)) { Color.WHITE } else { Color.parseColor("#B0B0B0") } }

/**
 * 動態的設置狀態欄顏色
 * 當顏色為白色的時候顯示白底黑字
 * 其他顏色為其他顏色底白色字
 * 一般由子類重寫
 */
fun setStatusBarColor(color: Int) {

    if (color == Color.WHITE) {
        //變黑色文字成功
        if (StatusBarUtils.setStatusBarBlackText(this)) {
            StatusBarUtils.setColor(this, Color.WHITE)
        } else {
            StatusBarUtils.setColor(this, Color.parseColor("#B0B0B0"))
        }

    } else {

        //變為白色文字成功
        StatusBarUtils.setStatusBarWhiteText(this)
        StatusBarUtils.setColor(this, color)

    }
}

fun setStatusBarBlackText(){
    StatusBarUtils.setStatusBarBlackText(this)
}

fun setStatusBarWhiteText(){
    StatusBarUtils.setStatusBarWhiteText(this)
}

```

我們可以再基類中使用默認的狀態欄背景顏色、文本顏色。然後子類想重寫的話直接調用方法設置即可,一般來説就能滿足我們的需求。

關於狀態欄文本顏色6.0以下是無法修改為黑色的文本的(沒有使用魅族小米的兼容方案,沒必要,如果大家想要魅族小米4.4 - 6.0的黑色文本兼容,可以網上找找,也可以留言),需要做一下兼容,當低版本設置狀態欄背景顏色為白色的時候,我們修改為灰色展示即可。

image.png

通過基類的封裝,我們確實可以很方便的實現狀態欄中背景顏色與文本顏色的處理,那麼沉浸式的處理如何解決呢?

1.3 系統狀態欄的沉浸式處理

一般沉浸式的處理,我們在oncreate方法中調用工具類方法。

kotlin StatusBarUtils.setStatusBarWhiteText(this) StatusBarUtils.immersive(this)

確實是實現了沉浸式,一般情況下也就夠用了,但是如果我們需求變化,或者框架變化,就會有問題,這樣的沉浸式如果設置了就不能取消,如果我們的場景是頂部的Title和狀態欄是一張圖片背景,然後下面的列表滾動到圖片消失之後狀態欄不沉浸式了,狀態欄背景顏色變為白色,這...

或者我們使用的是單Activity+多Fragment的框架,我們一個Activity中的根Fragment設置為沉浸式,那麼在其他的子Fragment中我們又不需要沉浸式。這...

一般在這樣的方案中我們可以通過自定義的狀態欄View,和自定義的TitleBar的解決。

可以使用標準TitleBar的頁面,我們可以通過TitleBar設置對應的狀態欄,例如:

kotlin <com.guadou.lib_baselib.view.titlebar.EasyTitleBar android:layout_width="match_parent" android:layout_height="wrap_content" app:Easy_hasStatusPadding="true" app:Easy_title="吐司-彈窗-banner" app:Easy_titleBarBackground="#ff0000" />

例如 hasStatusPadding 屬性。就是可以自由的設置TitleBar頂部的狀態欄間距。就可以自由的控制狀態欄的高度與顏色或背景的變化。

但是如果我們頂部是自定義的佈局,比如搜索框 + lottie動畫之類的整體佈局,那我們怎麼辦?

此處就需要使用我們自定義的狀態欄佈局:

```java public class StatusbarGrayView extends View {

public StatusbarGrayView(Context context) {
    super(context);
}

public StatusbarGrayView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public StatusbarGrayView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int widthMode = MeasureSpec.getMode(widthMeasureSpec); //得到寬度設置模式
    int widthSize = MeasureSpec.getSize(widthMeasureSpec); //得到寬度設置模式
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); //得到高度設置模式

    //如果設置高度為wrap-content自適應 那麼固定設置為狀態欄高度
    if (heightMode == MeasureSpec.AT_MOST) {
        if (widthMode == MeasureSpec.EXACTLY) {
            setMeasuredDimension(widthSize, EasyUtil.getStateBarHeight(getContext()));
        } else {
            setMeasuredDimension(1, EasyUtil.getStateBarHeight(getContext()));
        }
    } else {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    ViewGroup parent = (ViewGroup) getParent();
    if (parent == null) return;
    Drawable drawable = parent.getBackground();
    if (drawable instanceof ColorDrawable) {
        int color = ((ColorDrawable) drawable).getColor();

        if (color == Color.WHITE || color == CommUtils.getColor(R.color.white)) {

            setBackgroundColor(Build.VERSION.SDK_INT < 23 ? CommUtils.getColor(R.color.status_bar_gray_bg) : Color.WHITE);

        } else {
            setBackgroundColor(Color.TRANSPARENT);
        }
    } else {
        setBackgroundColor(Color.TRANSPARENT);
    }

}

}

```

我們使用自定義View,放在自定義搜索框Title的上面即可實現狀態高度的自由控制,

到此我們就能把Activity全部沉浸式,然後由自定義的TitleBar來管理我們的狀態,或者有自定義狀態View來管理狀態欄。

到此係統狀態欄的管理已經能實現絕大部分的效果處理,缺點是實現的方案不統一嗎,不同的效果需要不同的方案來解決,如果是別人接手你的項目可能會比較蒙。

而另一種方案也是我比較推薦的方案就是使用宿主的形式代替系統的狀態欄佈局。

二、自定義宿主管理狀態欄

具體的方案是,我們setContentView的時候,把系統的DecorView中的View取出,替換成我們自定義的佈局,內部包括一個自定義的狀態欄,和一個FramLayout,我們把之前的佈局取出添加到我們自定義佈局中,然後默認把當前Activity沉浸式之後,當下操作的StatusBar就是我們的自定義StatusBarView了。

這樣操作狀態欄就是操作我們的View對象了,想怎麼玩就怎麼玩,更加靈活了,設置顏色,Drawable,Alpha等都是非常的方便,想設置沉浸式直接Gone掉我們的自定義StatusBarView即可,想不用沉浸式,那麼就把StatusBarView設置為VISIBLE即可。

並且對狀態欄的各種操作不受到系統版本影響,就一個字,靈活!

定義我們自定義狀態欄View對象 ```java /* * 自定義狀態欄的View,用於StatusBarHostLayout中使用 / class StatusView extends View {

private int mBarSize;

public StatusView(Context context) {
    this(context, null, 0);
}

public StatusView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public StatusView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    mBarSize = StatusBarHostUtils.getStatusBarHeight(context);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mBarSize);
}

//獲取到當前的狀態欄高度
public int getStatusBarHeight() {
    return mBarSize;
}

} ```

核心的宿主代替類: ```java /* * 宿主的佈局 / @SuppressLint("ViewConstructor") public class StatusBarHostLayout extends LinearLayout {

private Activity mActivity;
private StatusView mStatusView;
private FrameLayout mContentLayout;

StatusBarHostLayout(Activity activity) {
    super(activity);

    this.mActivity = activity;

    //加載自定義的宿主佈局
    if (mStatusView == null && mContentLayout == null) {
        setOrientation(LinearLayout.VERTICAL);

        mStatusView = new StatusView(mActivity);
        mStatusView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        addView(mStatusView);

        mContentLayout = new FrameLayout(mActivity);
        mContentLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f));
        addView(mContentLayout);
    }

    //替換宿主的contentView為外界設置的View
    replaceContentView();

    //設置原生的狀態欄沉浸式,使用自定義的狀態欄佈局
    StatusBarHostUtils.immersiveStatusBar(mActivity);
    StatusBarHostUtils.setStatusBarColor(mActivity, Color.TRANSPARENT);
}

private void replaceContentView() {
    Window window = mActivity.getWindow();
    ViewGroup contentLayout = window.getDecorView().findViewById(Window.ID_ANDROID_CONTENT);
    if (contentLayout.getChildCount() > 0) {
        //先找到DecorView的容器移除掉已經設置的ContentView
        View contentView = contentLayout.getChildAt(0);
        contentLayout.removeView(contentView);
        ViewGroup.LayoutParams contentParams = contentView.getLayoutParams();

        //外部設置的ContentView添加到宿主中來
        mContentLayout.addView(contentView, contentParams.width, contentParams.height);
    }
    //再把整個宿主添加到Activity對應的DecorView中去
    contentLayout.addView(this, -1, -1);
}


/**
 * 設置狀態欄文本顏色為黑色
 */
public StatusBarHostLayout setStatusBarBlackText() {
    StatusBarHostUtils.setStatusBarDarkFont(mActivity, true);
    return this;
}

/**
 * 設置狀態欄文本顏色為白色
 */
public StatusBarHostLayout setStatusBarWhiteText() {
    StatusBarHostUtils.setStatusBarDarkFont(mActivity, false);
    return this;
}

/**
 * 設置自定義狀態欄佈局的背景顏色
 */
public StatusBarHostLayout setStatusBarBackground(int color) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        mStatusView.setBackgroundColor(color);
    } else {
        //6.0以下不能白色狀態欄
        YYLogUtils.w("當前的狀態顏色1:" + color);
        if (color == Color.WHITE) {
            color = Color.parseColor("#B0B0B0");
        }

        mStatusView.setBackgroundColor(color);
    }

    return this;
}

/**
 * 設置自定義狀態欄佈局的背景圖片
 */
public StatusBarHostLayout setStatusBarBackground(Drawable drawable) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        mStatusView.setBackground(drawable);
    } else {
        mStatusView.setBackgroundDrawable(drawable);
    }
    return this;
}

/**
 * 設置自定義狀態欄佈局的透明度
 */
public StatusBarHostLayout setStatusBarBackgroundAlpha(int alpha) {
    Drawable background = mStatusView.getBackground();
    if (background != null) {
        background.mutate().setAlpha(alpha);
    }
    return this;
}

/**
 * 給指定的佈局適配狀態欄高度,設置paddingTop
 */
public StatusBarHostLayout setViewFitsStatusBarView(View view) {

    //設置MaginTop的方式
    if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {

        //已經添加過了不要再次設置
        if (view.getTag() != null && view.getTag().equals("fitStatusBar")) {
            return this;
        }

        ViewGroup.MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
        int marginTop = layoutParams.topMargin;
        int setMarginTop = marginTop + mStatusView.getStatusBarHeight();
        view.setTag("fitStatusBar");
        layoutParams.topMargin = setMarginTop;
        view.requestLayout();
    }

    return this;
}


/**
 * 設置自定義狀態欄的沉浸式
 */
public StatusBarHostLayout setStatusBarImmersive(boolean needImmersive) {
    layoutimmersive(needImmersive);
    return this;
}

//具體的沉浸式邏輯
private void layoutimmersive(boolean needImmersive) {

    if (needImmersive) {
        mStatusView.setVisibility(GONE);
    } else {
        mStatusView.setVisibility(VISIBLE);
        mStatusView.setBackgroundColor(ContextCompat.getColor(mActivity, R.color.colorPrimary));
    }

}

} ```

定義一個狀態欄設置入口: ```java /* * 宿主替換佈局的方式管理狀態欄與內容的佈局 / public class StatusBarHost {

private StatusBarHost() {
}

public static StatusBarHostLayout inject(Activity activity) {
    Window window = activity.getWindow();
    ViewGroup contentLayout = window.getDecorView().findViewById(Window.ID_ANDROID_CONTENT);
    if (contentLayout.getChildCount() > 0) {
        View contentView = contentLayout.getChildAt(0);
        //如果當前是宿主的包裝類,直接強轉
        if (contentView instanceof StatusBarHostLayout) {
            return (StatusBarHostLayout) contentView;
        }
    }
    //如果不是我們封裝一個宿主包裝類
    return new StatusBarHostLayout(activity);
}

} ```

就簡單的三個類即可完成狀態欄的管理,內部的工具類和上面的工具類類似,只需要沉浸式的處理與狀態欄文本顏色的切換。

下面看看如何使用:

什麼都不加的默認效果

image.png

我們加上狀態欄顏色白色 kotlin val hostLayout = StatusBarHost.inject(this) .setStatusBarBackground(color(R.color.white)) .setStatusBarBlackText()

效果如下:

image.png

如果是6.0版本一下,是這樣的效果

image.png

切換狀態欄顏色為紅色

```kotlin findViewById(R.id.btn_bg_color).click {

        hostLayout.setStatusBarBackground(Color.RED)
    }

```

效果:

image.png

由於是我們自己的View,我們還能設置狀態欄背景Drawable ```kotlin findViewById(R.id.btn_bg_img).click {

        hostLayout.setStatusBarBackground(getDrawable(R.drawable.statusbar_image_1))
    }

```

效果如下:

image.png

6.0 一下的效果也是一樣的

image.png

沉浸式的處理,一句話就可以:

kotlin val hostLayout = StatusBarHost.inject(this) .setStatusBarImmersive(true)

效果:

image.png

那我們取消沉浸式: ```kotlin findViewById(R.id.btn_002).click {

        hostLayout.setStatusBarImmersive(false)
    }

```

效果:

image.png

我們可以自由的設置View的狀態欄間距

kotlin findViewById<View>(R.id.btn_003).click { hostLayout.setViewFitsStatusBarView(findViewById(R.id.title_view)) }

效果:

image.png image.png

滾動漸變狀態欄顏色: ```kotlin override fun init() {

    val startColor: Int = color(R.color.white)
    val endColor: Int = color(R.color.colorPrimary)

    //默認的狀態處理
    hostLayout = StatusBarHost.inject(this)
        .setStatusBarBackground(startColor)
        .setStatusBarWhiteText()


    //監聽滾動
    val myScrollView = findViewById<NestedScrollView>(R.id.my_scroll)
    easyTitleBar = findViewById(R.id.easy_title)
    easyTitleBar.setBackgroundColor(startColor)
    val topImg = findViewById<ImageView>(R.id.iv_top_img)

    SizeUtils.forceGetViewSize(topImg) {
        imgHeight = it.height
    }

    myScrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->

        if (imgHeight > 0) {

            val scrollDistance = Math.min(scrollY, imgHeight)

            val fraction: Float = scrollDistance.toFloat() / imgHeight.toFloat()

            setTitleStatusBarAlpha(CalculateUtils.evaluate(fraction, startColor, endColor))
        }

    })

}

private fun setTitleStatusBarAlpha(color: Int) {
    easyTitleBar.setBackgroundColor(color)
    hostLayout.setStatusBarBackground(color)
}

```

6.0 以上效果:

scroll_02.gif

6.0 以下效果:

scroll_04.gif

可以看到相比系統狀態欄的管理,我們使用宿主的方案一樣的可以全部實現,並且使用起來方式更加的統一,更加的便捷一些。

總結

兩種方案都可以,看大家習慣使用的是哪一種方案。

如果是管理系統的狀態欄,那麼我們在一些特殊場景和單Activity的架構中就沒有那麼靈活,需要配合自定義的TitleBar和自定義StatusBarView來處理。

如果是使用宿主方案管理狀態欄,那麼我們使用起來兼容性會更好一些,但是我們需要理解它實現的原理,才能更方便和靈活的做出對應的操作,需要有一些學習成本。

你們用的都是哪一種方案呢?

本文全部代碼都已全部貼出,如果大家有興趣也可以查看源碼自取。

好了,本期內容如講的不到位或錯漏的地方,希望同學們可以指出交流。

如果感覺本文對你有一點點點的啟發,還望你能點贊支持一下,你的支持是我最大的動力。

Ok,這一期就此完結。

「其他文章」