react-native android狀態列

Undo_03發表於2018-06-14

react-native 開發App的時候難免會遇到狀態,欄的,背景顏色和字型顏色與App內容頁面,色調適配,間言之就是將狀態列顏色與App顏色一致,使使用者介面更加整體。

1.android裝置系統元素

  • 導航欄:就是裝置頂部的網路、時間、電量等資訊欄
  • ActionBar: 返回按鈕以及系統預設的header區域,RN開發中一般不會用到,RN中在navigation中進行定製
  • 導航欄: 裝置下方的物理返回、回桌面、選擇應用程式等系統導航欄

2.狀態列的呈現形式

  • 預設展示,一直顯示手機系統的狀態列
  • 透明狀態列,狀態列背景顏色透明,狀態列顏色與App顏色一致,使用者介面更加整體。
  • 隱藏狀態列(沉浸式),狀態列完全隱藏,類似於全屏遊戲、視訊播放器的效果

2.1 預設展示

系統預設狀態列樣式,無法改變

2.2 透明狀態列

透明狀態列很常見,大多數的App都是使用這種模式,使得狀態列顏色與App顏色一致,使使用者介面更加整體,整個應用看起來更加美觀。

實現透明的狀態列的方式很多:

一、使用App的主題進行配置,在app/main/res/values/styles.xml中設定主題

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item> // 設定狀態列不佔據空間
        // <item name="android:windowLightStatusBar">true</item> // 設定狀態列字型顏色
    </style>

</resources>

複製程式碼

這種方式支援api19, 即Android4.4及以上,會在App啟動的時候就生效, 在App啟動時有許可權確認、系統彈窗等也不受影響,在彈出modal之類的深色蒙層時狀態列字型會變成成淺色

只設定<item name="android:windowTranslucentStatus">true</item>這種方式設定的透明狀態列,狀態列字型預設白色,無法再動態通過StatusBar改變狀態列的背景顏色,在做需要改變狀態列背景顏色的時候就比較尷尬了

再加一個<item name="android:windowLightStatusBar">true</item>這樣設定狀態列字型顏色之後,在深色modal彈出的時候字型不會動態改變成白色,但可以通過StatusBar設定barStyle來改變,實際上也不是很方便

二、android原生設定,在MainActivity的onCreate中進行設定

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 設定透明狀態列
    if (Build.VERSION.SDK_INT >= 21) {
        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);
    }
    
    // 設定透明狀態列和透明導航欄
    if (Build.VERSION.SDK_INT >= 21) {
	    View decorView = getWindow().getDecorView();
	    int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
	            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
	            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
	    decorView.setSystemUiVisibility(option);
	    getWindow().setNavigationBarColor(Color.TRANSPARENT);
	    getWindow().setStatusBarColor(Color.TRANSPARENT);
	}
}
複製程式碼

透明式狀態列,只有5.0及以上系統才支援,因此這裡先進行了一層if判斷,只有系統版本大於或等於5.0的時候才會執行下面的程式碼。 接下來我們使用了SYSTEM_UI_FLAG_LAYOUT_FULLSCREENSYSTEM_UI_FLAG_LAYOUT_STABLE,注意兩個Flag必須要結合在一起使用,表示會讓應用的主體內容佔用系統狀態列的空間,也就是說狀態列不再佔據空間。最後再呼叫Window的setStatusBarColor()方法將狀態列設定成透明色就可以了。

三、使用RN的StatusBar來設定,在App首次載入的頁面中對狀態列進行設定

<StatusBar backgroundColor='transparent' translucent barStyle={'dark-content'} />
複製程式碼

這種方式,會在App剛啟動的時候和App啟動時有許可權確認、系統彈窗等會先試用系統的預設狀態列,載入App頁面之後再改變成上面設定的樣式。 好處在於可以動態進行設定狀態列的樣式。

StatusBar屬性簡介:

  • animated: bool 指定狀態列的變化是否應以動畫形式呈現。目前支援這幾種樣式:backgroundColor, barStyle和hidden
  • hidden: bool 是否隱藏狀態列。
  • backgroundColor: 狀態列的背景色。
  • translucent: bool 指定狀態列是否透明。設定為true時,應用會在狀態列之下繪製(即所謂“沉浸式”——被狀態列遮住一部分)。常和帶有半透明背景色的狀態列搭配使用。
  • barStyle: enum('default', 'light-content', 'dark-content') 設定狀態列文字的顏色。

以上幾種方式都會有一個問題,狀態列不再佔據空間,因此在頁面佈局的時候需要加 paddingTop 值為狀態列的高度。

純前端就可以實現,這也是適配目前主流劉海屏的一種方式,利用StatusBar.currentHeight可以獲取到裝置狀態列的高度。

2.3 隱藏 狀態列 和 導航欄

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();

複製程式碼

3. 淺色狀態列的相容性配置

目前市面上的淺色狀態列基本都是 白底黑字, 支援這種設定的有Android6.0及其以上; MIUI v6及以上, Flyme 4.0及以上

具體相容方案如下:

Flyme 4.0及以上

 public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
    boolean result = false;
    if (window != null) {
        try {
            WindowManager.LayoutParams lp = window.getAttributes();
            Field darkFlag = WindowManager.LayoutParams.class
                    .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
            Field meizuFlags = WindowManager.LayoutParams.class
                    .getDeclaredField("meizuFlags");
            darkFlag.setAccessible(true);
            meizuFlags.setAccessible(true);
            int bit = darkFlag.getInt(null);
            int value = meizuFlags.getInt(lp);
            if (dark) {
                value |= bit;
            } else {
                value &= ~bit;
            }
            meizuFlags.setInt(lp, value);
            window.setAttributes(lp);
            result = true;
        } catch (Exception e) {

        }
    }
    return result;
}
複製程式碼

Android6.0及以上

 public static void setAndroidNativeLightStatusBar(Activity activity, boolean dark) {
    //狀態列字型圖示顏色
    View decor = activity.getWindow().getDecorView();
    if (dark) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR //淺色狀態列(字型圖示白色)
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN //contentView 全屏(置於statusbar之下)
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        }
    } else {
        decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
    }
}
複製程式碼

MIUI v6及以上

public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
    if(Build.VERSION.SDK_INT >= 24){
        return false;
    }
    boolean result = false;
    Window window=activity.getWindow();
    if (window != null) {
        Class clazz = window.getClass();
        try {
            int darkModeFlag = 0;
            Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
            Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
            darkModeFlag = field.getInt(layoutParams);
            Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
            if(dark){
                extraFlagField.invoke(window,darkModeFlag,darkModeFlag);//狀態列透明且黑色字型
            }else{
                extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字型
            }
            result=true;

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //開發版 7.7.13 及以後版本採用了系統API,舊方法無效但不會報錯,所以兩個方式都要加上
                if(dark){
                    activity.getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                }else {
                    activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
                }
            }
        }catch (Exception e){

        }
    }
    return result;
}
    
複製程式碼

在MainActivity的onCreate中呼叫

LightStatusBarUtil.FlymeSetStatusBarLightMode(this.getWindow(), false);
LightStatusBarUtil.MIUISetStatusBarLightMode(this, false);   
LightStatusBarUtil.setAndroidNativeLightStatusBar(this, true);
複製程式碼

總結

實現透明狀態列,以上方案都沒有完全相容android 4.4以下版本,個人覺得比較合適的做法是 android設定透明狀態列 + 淺色狀態列的相容性配置 + StatusBar 來配合控制

相關文章