一、概述
关于状态栏的讨论很多,我们今天从需求的角度来看一下对于状态栏的知识点:
- 不显示状态栏
(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 知识梳理目录:http://www.jianshu.com/p/fd82d18994ce
- 个人主页:http://lizejun.cn
- 个人知识总结目录:http://lizejun.cn/categories/