状态栏(StatusBar)与导航栏(NavigationBar)沉浸式
前言:ActionBar与ToolBar
原理
导航栏
导航栏我们在做应用层开发的时候一般很少去设置或者额外的操作,采用的是系统默认的白色。
1:在5.0之前我们无法设置导航栏的颜色,5.0之后我们可以设置导航栏的颜色
2:当我们为Window窗口设置FLAG_TRANSLUCENT_NAVIGATION,在5.0之前表现最下边会有阴影效果,表现不是很好。5.0之后不会有阴影效果
3:当设置了FLAG_TRANSLUCENT_NAVIGATION,我们铺满布局的下方会与导航栏重合。
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
/**
* Window flag: request a translucent navigation bar with minimal system-provided
* background protection.
*
* This flag can be controlled in your theme through the
* {@link android.R.attr#windowTranslucentNavigation} attribute; this attribute
* is automatically set for you in the standard translucent decor themes
* such as
* {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor},
* {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor},
* {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and
* {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.
*
* When this flag is enabled for a window, it automatically sets
* the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.
*/
public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
实现方式以下两种
代码
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
theme 中设置
- true
android4.4表现效果图如下:
android5.0表现效果图如下:
状态栏
记得自己在5.0之前开发app,大部分状况关于状态栏我们一般全是采用系统默认,不会做任何处理,一般是黑色的。当时认为这也很好看的,也没这个沉浸式概念。当然在Android4.4之前,我们的应用没法改变手机的状态栏颜色,当我们打开应用时在屏幕的顶部有一条黑色的状态栏,google在Android4.4以后提供了设置沉浸式状态栏的方法,在5.0之前其实际的效果就是透明的状态栏,然后在状态栏的位置用我们自己的控件视图作显示,其颜色通常为应用的actionbar或者自己定义的标题栏的颜色,或者是将应用的整体的一张图片也占据到状态栏中。
在5.0之后我们也采用了浸式状态栏,6.0之前,这时候有的ui小姐姐效果图会出现白色的状态栏,导航栏的文字 、图标信息全部变为深黑。要知道在6.0之前除了特定的小米 、魅族手机可以实现外,当时也不知道如何做了。
- 解释:设置状态栏透明,并且变为全屏模式。上面的解释已经说得很清楚了,当window的这个属性有效的时候,会自动设置 system ui visibility的标志SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 。
根据开发体验沉浸式可以分成三个阶段: Android关于沉浸式状态栏总结
- Android4.4(API 19) - Android 5.0(API 21):
这个阶段可以实现沉浸式,但是表现的效果还不是很好,实现方式为: 通过 FLAG_TRANSLUCENT_STATUS 设置状态栏为透明并且为全屏模式,然后通过添加一个与StatusBar 一样大小的View,将View 的 background 设置为我们想要的颜色,从而来实现沉浸式。
- Android 5.0(API 21)以上版本:
在Android 5.0的时候,加入了一个重要的属性和方法 android:statusBarColor (对应方法为 setStatusBarColor),通过这个方法我们就可以轻松实现沉浸式。也就是说,从Android5.0开始,系统才真正的支持沉浸式。
- Android 6.0(API 23)以上版本:
其实Android6.0以上的实现方式和Android 5.0 +是一样,为什么要将它归为一个单独重要的阶段呢?是因为从Android 6.0(API 23)开始,我们可以改状态栏的绘制模式,可以显示白色或浅黑色的内容和图标(除了魅族手机,魅族自家有做源码更改,6.0以下就能实现)
1:Android4.4(API 19) - Android 5.0(API 21)实现沉浸式的方式
Android 4.4 WindowManager中新增了一个重要的属性 :FLAG_TRANSLUCENT_STATUS
/**
* Window flag: request a translucent status bar with minimal system-provided
* background protection.
*
* This flag can be controlled in your theme through the
* {@link android.R.attr#windowTranslucentStatus} attribute; this attribute
* is automatically set for you in the standard translucent decor themes
* such as
* {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor},
* {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor},
* {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and
* {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.
When this flag is enabled for a window, it automatically sets
* the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
*/
public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
- 解释:设置状态栏透明,并且变为全屏模式。上面的解释已经说得很清楚了,当window的这个属性有效的时候,会自动设置 system ui visibility的标志SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 。
有两种方式实现这个属性:
可以在代码中设置,如下:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
当然也可以在theme 中设置属性windowTranslucentStatus,如下:
- true
效果如下:
这里有两个问题
1:标题栏和状态栏重叠了,相当于整个布局上移了StatusBar 的高度
2:顶部会有暗色阴影
问题1的解决,标题栏的上方添加一个大小和StatusBar大小一样的View,View 的BackgroundColor 为标题栏一样的颜色,这个View起到一个占位的作用。这个时候,标题栏就会下移StatusBar的高度,回到正常的位置。
代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Window window = getWindow();
//android 5.0以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(ContextCompat.getColor(this,R.color.colorPrimary));
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup systemContent = findViewById(android.R.id.content);
View statusBarView = new View(this);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight());
statusBarView.setBackgroundColor(ContextCompat.getColor(this,R.color.colorPrimary));
systemContent.getChildAt(0).setFitsSystemWindows(true);
systemContent.addView(statusBarView, 0, lp);
}
}
private int getStatusBarHeight() {
Class> c;
Object obj;
Field field;
int x, statusBarHeight = 0;
try {
c = Class.forName("com.android.internal.R$dimen");
obj = c.newInstance();
field = c.getField("status_bar_height");
x = Integer.parseInt(field.get(obj).toString());
statusBarHeight =getResources().getDimensionPixelSize(x);
} catch (Exception e1) {
e1.printStackTrace();
}
return statusBarHeight;
}
问题2的解决,在小于5.0之前没有找到去掉阴影的方法。5.0之后,系统不会有这样的效果了。
如果是一张图片延伸到状态栏的话,直接设置FLAG_TRANSLUCENT_STATUS就可以了
android 4.4表现效果
android 5.0表现效果
小结:Android4.4上实现沉浸式状态栏的套路是:为window添加FLAG_TRANSLUCENT_STATUS Flag,然后添加一个和status bar 一样大小的View 站位,从而让让标题栏不会与status bar 重叠。而图片延伸到状态栏只需要设置FLAG_TRANSLUCENT_STATUS就OK。导航栏的的操作同样与状态栏类似,只不过在开发过程中遇到的情况较少,一般系统默认。
2.2 Android 5.0(API 21)以上实现沉浸式的方式
Android 5.0 是一个里程碑式的版本,从Android 5.0开始,Google 推出了全新的设计规范 Material Design,并且原生控件就可以实现一些炫酷的UI动效。在5.0中默认就支持我们所谓的沉浸式,新建一个5.0的项目。
说明:Theme.AppCompat.Light.NoActionBar,在5.0之后我们不会使用ActionBa,取而代之的是ToolBar。
各个颜色的指向可以参考
AndroidStudio之Theme、colorPrimary、colorPrimaryDark、colorAccent详解
android5.0之后,google 加入了一个比较重要的方法 setStatusBarColor (对应属性:android:statusBarColor),通过这个方法,可以很轻松地实现沉浸式状态栏,对于导航栏是setNavigationBarColor(对应属性:android:navigationBarColor)。具体实现如下
getWindow().setStatusBarColor(Color.RED);
getWindow().setNavigationBarColor(Color.RED);
效果如下
图片延伸到状态栏
从这个版本开始,google 加入了一个比较重要的方法 setStatusBarColor (对应属性:android:statusBarColor),通过这个方法,可以很轻松地实现沉浸式状态栏。方法如下:
在window类中
/**
* Sets the color of the status bar to {@code color}.
*
* For this to take effect,
* the window must be drawing the system bar backgrounds with
* {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
* {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set.
*
* If {@code color} is not opaque, consider setting
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
* {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
*
* The transitionName for the view background will be "android:status:background".
*
*/
public abstract void setStatusBarColor(@ColorInt int color);
想要这个方法有效果,必须设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,而且FLAG_TRANSLUCENT_STATUS不可使用 (Android 4.4才用这个)
解释:设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,表明会Window负责系统bar的background 绘制,绘制透明背景的系统bar(状态栏和导航栏),然后用getStatusBarColor()和getNavigationBarColor()的颜色填充相应的区域。这就是Android 5.0 以上实现沉浸式导航栏的原理。
实现方式以下两种
1:代码实现
//android 5.0以上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(ContextCompat.getColor(this,R.color.colorPrimary));
}
2 Theme中配置
- false
- true
- @color/colorPrimary
效果如下:
图片延伸到状态栏
效果图如下:
在Android 5.0 使图片延伸到状态栏,只需设置windowTranslucentStatus,将 statusBarColor 设置为透明即可:
前提知识点:
1:状态栏和导航栏窗口是系统级窗口而Activity对应的时应用窗口,它们属于不同的窗口层级
2: 状态栏和导航栏系统级窗口是在App应用窗口之上,Activity应用窗口虽然有整个屏幕的大小,
但是可显示内容的区域得除去其上叠加的不透明的窗口区域(正常状态下也就是状态栏下方导航栏上方区域,
当然是可以设置叠加显示的)
Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析
https://blog.csdn.net/luoshengyang/article/details/8479101/
window.getDecorView().setSystemUiVisibility(int visibility)这个方法参数表示的状态有如下
1:View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
Activity在导航栏上部全屏显示,但状态栏不会被隐藏覆盖,状态栏依然可见,Activity顶端布局部分会被状态栏遮住。
2:View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
Activity在状态栏下部全屏显示,但导航栏不会被隐藏覆盖,导航栏依然可见,Activity下端布局部分会被导航栏遮住。
3:View.SYSTEM_UI_FLAG_LAYOUT_STABLE
防止系统栏(包含导航栏与状态栏)隐藏时内容区域大小发生变化
4:View.SYSTEM_UI_FLAG_VISIBLE
显示状态栏,Activity不全屏显示(恢复到有状态的正常情况)。
5:View.INVISIBLE
隐藏状态栏,同时Activity会伸展全屏显示。
6:View.SYSTEM_UI_LAYOUT_FLAGS
效果同1一样
7:View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
隐藏虚拟按键(导航栏)。
8:View.SYSTEM_UI_FLAG_LOW_PROFILE
状态栏显示处于低能显示状态(low profile模式),状态栏上一些图标显示会被隐藏。
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
Flag表明这个窗口负责绘制系统状态栏标题栏的背景。如果设置,系统bar(包含状态栏与导航栏)绘制成透明背景,
在这个窗口相应的地方会填充{@link Window#getStatusBarColor()}
和{@link Window#getNavigationBarColor()}对应的颜色。
通过代码实现如下:
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
//activity视图只在状态栏下部与导航栏上部区域显示
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
//Activity在导航栏上部全屏显示,但状态栏不会被隐藏覆盖,状态栏依然可见,Activity顶端布局部分会被状态栏遮住。
//Activity在状态栏下部全屏显示,但导航栏不会被隐藏覆盖,导航栏依然可见,Activity下端布局部分会被导航栏遮住。
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
/*
* 表明这个窗口负责绘制系统状态栏标题栏的背景。如果设置,系统bar绘制成透明背景,在这个窗口相应的地方会填充
* {@link Window#getStatusBarColor()}和{@link Window#getNavigationBarColor()}对应的颜色*/
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
}
setContentView(R.layout.activity_main);
效果图如下:
2.3 Android 6.0 + 实现状态栏字色和图标浅黑色
使用沉浸式的时候会遇到一个问题,那就是Android 系统状态栏的字色和图标颜色为白色,当我的主题色或者图片接近白色或者为浅色的时候,状态栏上的内容就看不清了。 ,这个问题在Android 6.0的时候得到了解决。Android 6.0 新添加了一个属性SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
解释:为setSystemUiVisibility(int)方法添加的Flag,请求status bar 绘制模式,它可以兼容亮色背景的status bar 。要在设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDSflag ,同时清除了FLAG_TRANSLUCENT_STATUSflag 才会生效。
实现代码
//如果亮色,设置状态栏文字为黑色
if (isLightColor(color)) {
//暗色字体
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else {
//白色字体
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|View.SYSTEM_UI_FLAG_VISIBLE);
}
Theme主题中使用
- false
- true
- @android:color/holo_red_light
- true
推荐一个好的库
ImmersionBar