一、概述
關於狀態列的討論很多,我們今天從需求的角度來看一下對於狀態列的知識點:
- 不顯示狀態列
(4.4)
- 通過
Window
- 通過
DecorView
- 顯示狀態列
- 靜態設定狀態列透明
(4.4)
:windowTranslucentStatus
,需要解決重疊問題- 手動設定
padding
fitsSystemWindows
- 手動設定
- 設定狀態列顏色
(5.0)
:colorPrimaryDark
,style
中指定。setStatusBarColor
,動態設定。
- 設定狀態列和根佈局的層級關係
(4.4)
:SYSTEM_UI_FLAG_VISIBLE
:狀態列不覆蓋根佈局SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
:狀態列覆蓋根佈局
- 動態設定狀態列透明
(5.0)
- 改變狀態列圖示顏色
- 官方做法
(6.0)
- 魅族/小米做法
二、不顯示狀態列
不顯示狀態列一般用於螢幕可顯示區域較小的時候,並且要求可見面積較大的情況,例如視訊、橫屏等,而不顯示狀態列的方法有兩種,下面我們分別介紹一下:
2.1 通過Activity
所屬的Window
public static void setStatusBarVisibleByWindow(Activity activity, boolean visible) {
Window window = activity.getWindow();
if (window != null) {
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
if (visible) {
winParams.flags &= ~bits;
} else {
winParams.flags |= bits;
}
window.setAttributes(winParams);
}
}
複製程式碼
2.2 通過DecorView
public static void setStatusBarVisibleByDecorView(Activity activity, boolean visible) {
Window window = activity.getWindow();
if (window != null) {
if (visible) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
} else {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
}
}
}
複製程式碼
##2.3 小結 這兩種方法不可以混合使用,也就是說,不能通過其中一種方法將狀態列隱藏,又利用另一種方法將它顯示出來。
三、顯示狀態列
在顯示狀態列的情況下,我們將需求分為以下三類:
3.1 設定狀態列為透明
要求安卓版本大於4.4。
我們可以在style
中指定,然後設定給Activity
或者Application
:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
</style>
複製程式碼
當然,也可以通過程式碼來指定:
public static void setStatusBarTransparent(Activity activity) {
Window window = activity.getWindow();
if (window != null) {
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |= bits;
window.setAttributes(winParams);
}
}
複製程式碼
解決狀態列透明時和根佈局重疊的問題 這兩種方法是等效的,但是它們都存在一個問題,根佈局會和狀態列重疊,這時候就有兩種解決辦法:
3.1.1 在佈局中加上和狀態列高度相同的padding
這種方法有一點不好,就是我們需要事先知道狀態列的高度是多少,或者動態地獲取,並動態設定padding
。
3.1.2 fitsSystemWindows
為了解決上面的情況,我們可以通過fitsSystemWindows
屬性讓系統自動根據狀態列的高度來給我們的佈局留下足夠的空間。
- 在根佈局的
xml
中配置android:fitsSystemWindows
屬性:
<LinearLayout android:fitsSystemWindows="true">
複製程式碼
3.1.3 小結
需要注意,使用上面的方法來試狀態列透明,需要在Activity
的onCreate
之前設定,如果在之後設定,那麼可能會出現意料之外的結果,如果想要在程式碼當中設定狀態列透明,那麼建議採用3.2
和3.3
的結合版本,也就是改變狀態列顏色為透明 + 改變佈局,我們分析完下面的兩種之後,再看一下怎麼在程式碼中設定。
3.2 改變狀態列的顏色
在狀態列不透明的情況下,我們可以通過下面的方法來改變狀態列的顏色,要求Android
版本大於5.0
:
3.2.1 通過style
設定
其中colorPrimaryDark
就是指定的狀態列顏色
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@android:color/holo_green_light</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
複製程式碼
這裡有一點:如果我們設定了上面的屬性,並且設定了windowTranslucentStatus
,即使不將fitSystemWindow
設為true
,也不會使狀態列和根佈局重疊。
3.2.2 通過程式碼動態設定
public static void setStatusBarColor(Activity activity, int colorId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
if (window != null) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(activity.getResources().getColor(colorId));
}
}
}
複製程式碼
3.2.3 小結
由於上面討論的兩種方法都只能在5.0
以上實現,因此如果我們希望在5.0
以下動態改變狀態列的顏色,那麼就只能通過3.1
中的方法,把狀態列設定為透明,然後在佈局中新增一個和狀態列高度相同的背景,通過動態改變這個背景的顏色,來實現狀態列顏色的改變。
3.3 改變狀態列和根佈局的關係
其實在上面的兩種方法中,我們也間接地改變了狀態列和根佈局的關係,當然,我們也可以單純改變佈局關係,而不對狀態列作出任何改變:
public static void setStatusBarOverlay(Activity activity, boolean overlay) {
Window window = activity.getWindow();
if (window != null) {
if (overlay) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
} else {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
}
複製程式碼
3.4 動態設定狀態列透明
由於這種方式結合了3.2
和3.3
,因此要求版本要大於5.0
:
public static void setStatusBarTransparentDynamic(Activity activity, boolean overlay) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
if (window != null) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
if (!overlay) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}
window.setStatusBarColor(Color.TRANSPARENT);
}
}
}
複製程式碼
四、改變狀態列圖示顏色
4.1 官方標準做法
這種方法要求Android
的API
大於6.0
:
public static void setStatusBarItemColor(Activity activity, boolean dark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
if (window != null) {
final int bits = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
int currentBits = window.getDecorView().getSystemUiVisibility();
if (dark) {
currentBits |= bits;
} else {
currentBits &= ~bits;
}
window.getDecorView().setSystemUiVisibility(currentBits);
}
}
}
複製程式碼
4.2 其它廠商做法
五、小結
5.1
總結下來,在顯示狀態列的情況下,適配範圍最廣的是下面這種方法:
- 通過
DecorView
設定成透明並且重疊的模式(如3.4
) - 在根佈局中新增一個佈局,這個佈局的高度等於狀態列的高度,這樣我們就可以通過控制它的高度和顏色來實現多種組合。
- 如果想要改變圖示的顏色,就只能在
6.0
或者廠商的方法。
5.2
對於狀態列的操作主要是兩種:一種是對DecorView
操作,另一種是對window
操作,使用的時候對除了設定狀態列顏色外的操作一定要保持統一,一定不要同時進行這兩種方法來進行相反的操作。舉個例子,如果採用了3.1
中的對window
進行操作使得狀態列透明,並通過fitSystemWindows
改變佈局,那麼就不要再採用3.3
中DecorView.setSystemUiVisibility()
的方式來改變佈局的結構,最好是成對地出現,否則會使得佈局出現意料之外的情況。
5.3
最後附上程式碼:
public class StatusBarUtils {
public static void setStatusBarVisibleByWindow(Activity activity, boolean visible) {
Window window = activity.getWindow();
if (window != null) {
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
if (visible) {
winParams.flags &= ~bits;
} else {
winParams.flags |= bits;
}
window.setAttributes(winParams);
}
}
public static void setStatusBarVisibleByDecorView(Activity activity, boolean visible) {
Window window = activity.getWindow();
if (window != null) {
if (visible) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
} else {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
}
}
}
public static void setStatusBarTransparent(Activity activity) {
Window window = activity.getWindow();
if (window != null) {
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
winParams.flags |= bits;
window.setAttributes(winParams);
}
}
public static void setStatusBarTransparentDynamic(Activity activity, boolean overlay, boolean transparent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
if (window != null) {
if (transparent) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
if (!overlay) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}
window.setStatusBarColor(Color.TRANSPARENT);
}
}
}
}
public static void setStatusBarBackgroundColor(Activity activity, int colorId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
if (window != null) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(activity.getResources().getColor(colorId));
}
}
}
public static void setStatusBarOverlay(Activity activity, boolean overlay) {
Window window = activity.getWindow();
if (window != null) {
if (overlay) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
} else {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
}
public static void setStatusBarItemColor(Activity activity, boolean dark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Window window = activity.getWindow();
if (window != null) {
final int bits = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
int currentBits = window.getDecorView().getSystemUiVisibility();
if (dark) {
currentBits |= bits;
} else {
currentBits &= ~bits;
}
window.getDecorView().setSystemUiVisibility(currentBits);
}
}
}
}
複製程式碼
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/