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_FULLSCREEN
和SYSTEM_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 來配合控制