還用第三方庫管理狀態列嗎?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,這一期就此完結。

「其他文章」