前言:
作業幾年下來,處理statusbar的機會和場景不算多,每次遇到具體業務問題時,都是去網上找一些api解決當前問題,但是從來沒有從根本上去了解statusbar的發展歷程,今天下定決心去徹底認識statusbar,
了解statusbar的發展需要分三個階段:
- Android4.4(API19 KitKat)以前:無法做任何事,是的,就是一坨黑色,
- Android4.4~Android5.0(API21 Lollipop):可以實作狀態欄的變色,但是效果還不是很好,主要實作方式是通過FLAG_TRANSLUCENT_STATUS這個屬性設定狀態欄為透明并且為全屏模式,然后通過添加一個與StatusBar 一樣大小的View,將View 設定為我們想要的顏色,從而來實作狀態欄變色,
- Android5.0~Android6.0(API23 Marshmallow): 系統才真正的支持狀態欄變色,系統加入了一個重要的屬性和方法 android:statusBarColor (對應方法為 setStatusBarColor),通過這個屬性可以直接設定狀態欄的顏色
- Android6.0以后:主要就是添加了一個功能可以修改狀態欄上內容和圖示的顏色(黑色和白色)
下面開始了解主要的API:
一、首先新建一個專案,注意把values/values-v19/values-v21中凡是在style.xml中 有關 windowTranslucentNavigation、windowTranslucentStatus、statusBarColor 都刪掉, 同時把布局帶有android:fitsSystemWindows注釋掉,
public class Main3Activity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main3Activity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp"
android:textColor="@android:color/holo_red_light"
android:text="測驗文案"
/>
<View
android:id="@+id/testview"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@color/colorPrimary"
tools:ignore="MissingConstraints" />
</LinearLayout>
跑出來的效果是:
從左到右分別是 API 19(4.4), 21(5.0), 29(9.+)

二、代碼中設定FLAG_TRANSLUCENT_STATUS后,請看如下效果:
public class Main3Activity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
4.4會全透明,頁面上頂,
模擬器4.4應該是有bug,看不出效果,建議使用真機

4.4以上是一樣的效果,頁面整體上頂,沉在狀態欄之下,同時狀態欄變為透明!
下圖左邊是 api21, 右邊是api29, 以下相同

這里我們給出一個結論:
android4.4—android5.0主要通過FLAG_TRANSLUCENT_STATUS這個屬性實作狀態欄變色,當使用這個flag時SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN會被自動添加,
這顯然不是我們想要的效果,我們試著讓狀態欄透明,同時頁面不被狀態欄遮擋
三、我們使用setFitsSystemWindows(android:fitsSystemWindows=“true”)這個api試試,
public class Main3Activity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup rootView = (ViewGroup) ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
rootView.setFitsSystemWindows(true);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true" //注意是這里!
tools:context=".Main3Activity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp"
android:textColor="@android:color/holo_red_light"
android:text="測驗文案"
/>
<View
android:id="@+id/testview"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@color/colorPrimary"
tools:ignore="MissingConstraints" />
</LinearLayout>
或者在XML區域中設定:


四、4.4沒有提供改變statusbar顏色的方案,我們采取了一個hack的方式,
方法是:要添加一個view填充在狀態欄上,view的高度就是狀態欄的高度,顏色就是你想要的狀態欄的顏色,
public class Main3Activity extends AppCompatActivity{
private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
View fakeStatusBarView = decorView.findViewById(R.id.statusbarutil_fake_status_bar_view);
if (fakeStatusBarView != null) {
if (fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
fakeStatusBarView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
} else {
decorView.addView(createStatusBarView(getResources().getColor(R.color.colorAccent)));
}
setRootView();
}
}
private void setRootView() {
ViewGroup parent = (ViewGroup) findViewById(android.R.id.content);
for (int i = 0, count = parent.getChildCount(); i < count; i++) {
View childView = parent.getChildAt(i);
if (childView instanceof ViewGroup) {
childView.setFitsSystemWindows(true);
((ViewGroup) childView).setClipToPadding(true);
}
}
}
private View createStatusBarView( @ColorInt int color) {
// 繪制一個和狀態欄一樣高的矩形
View statusBarView = new View(this);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight());
statusBarView.setLayoutParams(params);
statusBarView.setBackgroundColor(color);
statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
return statusBarView;
}
private int getStatusBarHeight() {
// 獲得狀態欄高度
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
return getResources().getDimensionPixelSize(resourceId);
}
}

注意:以下分析的api都與4.4無關了,4.4能做的事情就是對添加到頂部的view進行UI的變化,自主定制,思路都是一致的,
五、5.0(包含5.0)以上,設定statusbar的顏色可以直接利用一個window提供的api即可,不需要做任何限制,
注意:寫改代碼時一定要判斷sdk版本,不加判斷的話,運行到5.0以下機器上會直接crash
public class Main3Activity extends AppCompatActivity{
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
// 上面三行代碼,如果設定為全透明,則必須加上,設定為半透明,則可以不需要
getWindow().setStatusBarColor(getResources().getColor(R.color.colorAccent));
}
}
}

六、如果實作導航欄半透明,圖片作為布局底部,沉在導航欄下的效果,
public class Main3Activity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 設定狀態欄透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main3Activity">
<ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:src="@drawable/brand_pic_kr"
android:scaleType="centerCrop"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp"
android:textColor="@android:color/holo_red_light"
android:text="測驗文案"
/>
<View
android:id="@+id/testview"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@color/colorPrimary"
tools:ignore="MissingConstraints" />
</LinearLayout>

七、改變狀態欄文字顏色:
Android 6.0 以上使用系統方法修改狀態欄字體、圖示顏色;
Android 4.4 到 6.0 之間使用第三方系統提供的方法修改狀態欄字體、圖示顏色(目前只有 MIUI 和 Flyme),
setMIUIStatusBarDarkIcon(activity, true);
setMeizuStatusBarDarkIcon(activity, true);
//設定為黑色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
//設定為白色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
/**
* 修改 MIUI V6 以上狀態欄顏色
*/
private static void setMIUIStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) {
Class<? extends Window> clazz = activity.getWindow().getClass();
try {
Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
extraFlagField.invoke(activity.getWindow(), darkIcon ? darkModeFlag : 0, darkModeFlag);
} catch (Exception e) {
//e.printStackTrace();
}
}
/**
* 修改魅族狀態欄字體顏色 Flyme 4.0
*/
private static void setMeizuStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) {
try {
WindowManager.LayoutParams lp = activity.getWindow().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 (darkIcon) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
activity.getWindow().setAttributes(lp);
} catch (Exception e) {
//e.printStackTrace();
}
}
API29狀態欄文字設定為黑色的效果:

八、FLAG_FULLSCREEN的作用:
public class Main3Activity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
}
分別為api19,21,29

使狀態欄消失,比如小說閱讀APP中可以使用到,
不得不提一句,除此以外,還有很多個flag,其實很多api功能重疊,或者因為版本問題遺留,導致該問題的處理上沒有統一方案,大家找到適合自己的就好,
九、實作類似橫屏視頻播放的效果,默認不展示狀態欄,觸摸螢屏時顯示出來狀態欄,
參考:https://blog.csdn.net/sbsujjbcy/article/details/48391863
十、基本原理弄清楚以后,其實我們在實際使用中,可以推薦一個牛逼的輪子:
https://jaeger.itscoder.com/android/2016/03/27/statusbar-util.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/126544.html
標籤:AI
上一篇:交換機的軟體開發問題
下一篇:私有ip為什么還能ping到?
