沉浸式模式和透明化系统栏适配
沉浸式模式和透明化系统栏适配
适配基础
Translucent Bar(透明化系统栏) 和 Immersive Mode(沉浸式模式) 概念
沉浸式状态栏
,Android 官方从来没有给出过沉浸式状态栏这样的命名,只有沉浸式模式(Immersive Mode)这种说法。一般分 2 种说法:透明系统栏和沉浸式模式。一般使用透明系统栏比较多,沉浸式模式基本只有游戏,全屏视频才用得上。
Translucent system bars 透明化系统栏(透明状态栏,内容布局到系统栏)
透明化系统栏,使得布局侵入系统栏的后面,必须启用 fitsSystemWindows 属性来调整布局才不至于被系统栏覆盖。
Immersive full-screen mode 沉浸式 (全屏) 模式、隐藏状态栏和导航栏
隐藏 状态栏、ActionBar、导航栏
等使屏幕全屏,让 Activity 接收所有的(整个屏幕的)触摸事件。沉浸式状态栏的叫法是错误的,应该是一种沉浸式的模式。它要求隐藏状态栏,使屏幕全屏,常应用在游戏,视频播放场景。
适配的变更
- Android3.0 及以上增加了
setSystemUiVisibility
,后续版本增加了很多 SystemUI Flags - Android4.4 及以上开始支持透明化状态栏和沉浸模式
- Android5.0 及以上支持状态栏/导航栏变色
- Android6.0 及以上支持状态栏字体颜色变更
- Android11 及以上,所有 flags 都过时了,用 WindowInsetsController 来适配
fitSystemWindows 适配
将 WindowInsets分发
章节的 fitSystemWindows
主题中各种颜色
1
2
3
4
5
6
7
8
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@android:color/holo_blue_light</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
可能取消 System UI flags 的操作
- 触摸屏幕任何位置
- 顶部下拉状态栏
- 底部上拉导航栏
- Window 的变化 (如:跳转到其他界面、弹出键盘等)
沉浸式模式和透明化系统栏适配方案
QMUIStatusBarHelper,简单的工具类,方便 (推荐)
ImmersionBar 库太大,不方便定制
UltimateBarX
https://github.com/Zackratos/UltimateBarX
适配场景
沉浸式模式——隐藏状态栏
长时间隐藏状态栏(比如:游戏 App)
1
2
3
4
5
6
7
8
9
10
private fun hideStatusBar(on: Boolean) {
val winParams = window.attributes
val bits = WindowManager.LayoutParams.FLAG_FULLSCREEN
if (on) {//隐藏状态栏
winParams.flags = winParams.flags or bits
} else {//显示状态栏
winParams.flags = winParams.flags and bits.inv()
}
window.attributes = winParams
}
- 用户任何交互操作都不会使系统自动清除状态栏隐藏状态
- 顶部下拉状态栏操作会是状态栏暂时显示,延迟几秒后自动消失。
短暂时间隐藏状态栏(比如:阅读 App)
1
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_FULLSCREEN
会被以下场景打断:
2. 顶部下拉状态栏
4. Window 的变化 (如:跳转到其他界面、弹出键盘等)
沉浸式隐藏状态栏
1
2
3
4
5
private fun hideStatusBar(){
var tag = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
layout.systemUiVisibility = tag
}
- 用户的 Window 的变化 (如:跳转到其他界面、弹出键盘等) 操作会使系统自动清除状态栏隐藏状态(系统会自动取消
View.SYSTEM_UI_FLAG_FULLSCREEN
的设置,但是不会取消View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
); - 用户的 顶部下拉状态栏 操作会使状态栏暂时显示,延迟几秒后自动消失。
- 使用此 flag,系统会自动忽略输入法的
SOFT_INPUT_ADJUST_RESIZE
的特性。
沉浸式模式——隐藏导航栏
短暂时间隐藏导航栏
1
2
3
private fun hideNavigationBar() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}
用户的 任何操作
都会使系统自动清除此状态。SYSTEM_UI_FLAG_HIDE_NAVIGATION
被系统自动清除时会连带清除 SYSTEM_UI_FLAG_FULLSCREEN
隐藏状态栏 Immersive
1
2
3
4
5
private fun hideNavigationBarImmersive() {
val tag = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE)
window.decorView.systemUiVisibility = tag
}
下面操作会使系统自动清除此状态:
2. 顶部下拉状态栏
3. 底部上拉导航栏
4. Window 的变化 (如:跳转到其他界面、弹出键盘等)
触摸屏幕不会取消该 flag
隐藏状态栏 Immersive Sticky
1
2
3
4
5
private fun hideNavigationBarImmersiveSticky() {
val tag = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
window.decorView.systemUiVisibility = tag
}
Window的变化(如:跳转到其他界面、弹出键盘等)
操作会使系统自动清除此状态;用户的 顶部下拉状态栏
和 底部上拉导航栏
操作会是使导航栏暂时显示,延迟几秒后自动消失
沉浸式模式——全屏模式
当你确定要使用沉浸式模式,那么只需要重写 Activity 的 onWindowFocusChanged() 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
fullScreen1(); // 按需选择方法
// fullScreen2();
// fullScreen3()
}
}
用于视频播放的情形
1
2
3
4
5
6
7
8
9
10
private fun fullScreen1() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val flags = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or flags
}
}
- 状态栏和导航栏都被隐藏
- 用户的任何交互都会导致全屏状态被系统清除
- 注意内容布局到状态栏和导航栏被遮挡住
用于沉浸阅读的情形
1
2
3
4
5
6
7
8
9
10
11
private fun fullScreen2() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val flags = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_IMMERSIVE)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or flags
}
}
- 状态栏和导航栏都被隐藏
- 下拉通知栏和上拉导航栏都会退出,不会恢复
- 注意内容布局到状态栏和导航栏被遮挡住
用于游戏等严格沉浸的情形
1
2
3
4
5
6
7
8
9
10
private fun fullScreen1() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val flags = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or flags
}
}
- 状态栏和导航栏都被隐藏
- 下拉通知栏和上拉导航栏临时退出全屏,过个几秒又会恢复全屏
- 注意内容布局到状态栏和导航栏被遮挡住
透明化系统栏——(半) 透明状态栏/导航栏
Android4.4-Android5.0 实现状态栏半透明和导航栏半透明
- Android4.4-Android5.0 只能半透明状态栏和导航栏,不支持设置颜色(注意:不是全透明)
- 用这个 flag:
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
和FLAG_TRANSLUCENT_NAVIGATION
代码设置,或在 values-v19 设置主题
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
和WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
介绍:
1
2
3
4
5
0. 状态栏用`LayoutParams.FLAG_TRANSLUCENT_STATUS`,导航栏用`WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION`
1. 状态栏半透明
2. 也可以在主题中配置windowTranslucentStatus属性,效果一样;其中带有xxx_TranslucentDecor主题的默认设置了该flag
3. 设置了该flag,自动添加了View.SYSTEM_UI_FLAG_LAYOUT_STABLE和View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
4. 已经过时,大于等于Android21用Window.setStatusBarColor(Android5.0)设置一个半透明替代;Android4.4~Android5.0用这个flag
实现
- 主题方式实现:
1
2
3
4
<style name="ThemeTransparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
- 代码方式实现:
1
2
3
// 过时 Use Window.setStatusBarColor(int) with a half-translucent color instead.
this.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
this.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
在不同版本设置 WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
后的效果:
效果:
解决状态栏和标题栏重叠
存在的问题:
- 在 Android4.4 版本状态栏的顶部有一个渐变,会显示出黑色的阴影(底部的导航栏也是一样的效果);Android4.4 后的版本状态栏就是个半透明的
- 状态栏透明了,标题栏和状态栏重叠了
- 解决标题栏和状态栏重叠方式 1:添加属性
android:fitsSystemWindows="true"
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".samples.ui.屏幕适配相关.沉浸式_透明状态栏.沉浸式场景.沉浸到状态栏.主题方案.沉浸到状态栏_主题方案">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/red_A400"
android:fitsSystemWindows="true"
android:gravity="center"
android:text="标题栏"
android:textColor="@color/black"
android:textSize="20sp" />
</LinearLayout>
在 TextView 增加 paddingTop=状态栏高度:存在标题内容被挤压了问题;fitsSystemWindows 不能设置在 LinearLayout 上,否则状态栏颜色就不会和 TextView 颜色一致,需要设置在 TextView 上
- 解决标题栏和状态栏重叠方式 2:添加一个 View,高度为状态栏高度,背景为 TextView 的颜色
小结
- Android4.4 上实现沉浸式状态栏的套路是:为 window 添加
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS Flag
,然后添加一个和 status bar 一样大小的 View 占位,从而让让标题栏不会与 status bar 重叠。而图片延伸到状态栏只需要设置WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
就 OK WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
沉浸式在 Android4.4-Android5.0 之间的版本表现得不是很好,状态栏的顶部有一个渐变,会显示出黑色的阴影(底部的导航栏也是一样的效果)
Android5.0 及以上实现状态栏透明和导航栏透明
- 内容不会沉浸到状态栏和导航栏(没啥实际意义)
- 全透明
1
2
3
4
5
6
7
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.statusBarColor = resources.getColor(android.R.color.transparent)
window.navigationBarColor = resources.getColor(android.R.color.transparent)
}
效果:
[半]透明状态栏/导航栏
小结
1. 半透明状态栏 (API>=19)
1
2
3
4
5
6
7
private fun translateStatusBar(on: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
var p = window.attributes
p.flags = p.flags or WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
window.attributes = p
}
}
注意:为避免状态栏遮挡 View 上的部分信息,需要为 View 设置 fitSystemWindow=true,或手动设置 padding
2. 全透明状态栏并沉浸内容到状态栏,导航栏不沉浸 (API>=21)
1
2
3
4
5
6
7
8
9
10
11
12
private fun translateStatusBarAndNavigationBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = Color.TRANSPARENT
val tag = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or tag
} else {
toast("低于Android21,不做任务事情")
}
}
- Android 的状态栏的字体颜色默认为白色,只有 Android6.0 以后才提供官方的 API 选择黑色字体(
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
)。所以当 6.0 以下使用透明状态栏时,若 StatusBar 下面的 View 的也为白色背景时,则会造成看不到 StatusBar 的相关信息。 - 为避免状态栏遮挡 View 上的部分信息,需要为
View设置fitSystemWindow=true
,或手动设置 padding
3. 全透明状态栏和导航栏,内容布局到状态栏和导航栏 (API>=21)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private fun translateStatusBarAndNavigationBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
val tag =
(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or tag
} else {
toast("低于Android21,不做任务事情")
}
}
效果:
- 状态栏和导航栏会遮挡内容 view,在根布局设置
fitsSystemWindows="true"
Android 的状态栏的字体颜色默认为白色,又是透明状态栏和导航栏,导致看不清了
4、全版本适配透明状态栏和导航栏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void transparentStatusAndNavigation(Window window) {
// make full transparent statusBar
if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
setWindowFlag(window, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, true);
}
if (Build.VERSION.SDK_INT >= 19) {
int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
visibility = visibility | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
window.getDecorView().setSystemUiVisibility(visibility);
}
if (Build.VERSION.SDK_INT >= 21) {
int windowManager = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
windowManager = windowManager | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
setWindowFlag(window, windowManager, false);
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
}
}
private static void setWindowFlag(final int bits, boolean on) {
Window win = getWindow();
WindowManager.LayoutParams winParams = win.getAttributes();
if (on) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
win.setAttributes(winParams);
}
状态栏/导航栏变色,非沉浸到状态/导航栏
状态栏变色,只支持 Android5.0 及以上,Android4.4 不支持。
- flag
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
介绍
设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
,表明 Window 会负责系统 bar background 的绘制,如果设置了系统 bar(状态栏和导航栏)会被绘制成透明背景,然后用getStatusBarColor()
和getNavigationBarColor()
的颜色填充相应的区域 Window.statusBarColor/setNavigationBarColor(color)
介绍
- 设置状态栏的背景色,Android5.0 及以上(API21)可用
- window 必须 drawing the system bar backgrounds,通过设置
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
这个 flag,且WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
这个 flag 必须没有设置 - 如果 color 带有透明,考虑设置
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
和View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
实现
- 代码方式:
xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/red_A400"
android:gravity="center"
android:text="标题栏"
android:textColor="@color/black"
android:textSize="20sp" />
</LinearLayout>
代码:
1
2
3
4
5
6
7
8
9
10
11
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.statusBarColor = resources.getColor(R.color.red_A400)
window.navigationBarColor = resources.getColor(R.color.red_A400)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.navigationBarDividerColor = resources.getColor(R.color.black) // 设置导航栏和App的一条线
}
}
效果:
- 主题方式实现
1
2
3
4
5
<style name="MDTheme" parent="Theme.Design.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/red_A400</item>
</style>
图片沉浸到状态栏
Android4.4-Android5.0
布局:
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_girl" />
</LinearLayout>
代码:
1
2
3
4
5
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 过时 Use Window.setStatusBarColor(int) with a half-translucent color instead.
this.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
this.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
}
效果:
Android5.0 及以上效果就不对了。
实现状态栏字色和图标浅黑色 (Android 6.0+)
默认 Android 系统状态栏的字色和图标颜色为白色
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 介绍
- 设置状态栏 light 模式(设置为浅黑色字体)
- 需配置
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
flag,不要配置WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- 过时,用
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
替代
代码方式实现
1
2
3
4
5
6
7
8
9
10
11
12
// 设置light模式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.decorView.systemUiVisibility = /*View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or*/ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
// 清除light模式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val flag = window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
window.decorView.systemUiVisibility = flag
}
主题方式实现
在 values-v23
文件夹下添加该主题:
1
2
3
4
5
6
7
<style name="MDTheme" parent="Theme.Design.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/holo_red_light</item>
<!-- Android 6.0以上 状态栏字色和图标为浅黑色-->
<item name="android:windowLightStatusBar">true</item>
</style>
WindowInsetsController
见 WindowInsetsController.md
沉浸模式&透明系统栏坑
Android29 上透明导航栏颜色不对 Disabling system bar protection on Android 10
1
2
3
4
5
6
7
8
// 设置状态栏和导航栏透明的底色
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.navigationBarDividerColor = Color.RED
}
效果:
解决,在 v29 的 theme.xml 中添加:
1
2
3
4
<style name="Theme.Kingassist" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:enforceStatusBarContrast">false</item>
</style>
效果:
- Gesture Navigation: going edge-to-edge (I)
https://medium.com/androiddevelopers/gesture-navigation-going-edge-to-edge-812f62e4e83e
无法全透明导航栏(NavigationBar)
1
2
3
4
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
window.decorView.systemUiVisibility=(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.navigationBarColor = Color.TRANSPARENT
en 发现导航栏半透明背景依然无法去掉:
问题:当设置导航栏为透明色(Color.TRANSPARENT)时,导航栏会变成半透明,当设置其他颜色,则是正常的,例如设置颜色为 0x700F7FFF,显示效果如下:
分析:
1
2
3
4
5
6
7
8
9
10
11
// Activity.onApplyThemeResource
// Get the primary color and update the TaskDescription for this activity
TypedArray a = theme.obtainStyledAttributes(
com.android.internal.R.styleable.ActivityTaskDescription);
if (mTaskDescription.getPrimaryColor() == 0) {
int colorPrimary = a.getColor(
com.android.internal.R.styleable.ActivityTaskDescription_colorPrimary, 0);
if (colorPrimary != 0 && Color.alpha(colorPrimary) == 0xFF) {
mTaskDescription.setPrimaryColor(colorPrimary);
}
}
也就是说如果设置的导航栏颜色为 0(纯透明)时,将会为其修改为内置的颜色:
ActivityTaskDescription_colorPrimary
,因此就会出现灰色蒙层效果。
解决:将纯透明这种情况修改颜色为 0x01000000
,这样也能达到接近纯透明的效果:
亮色系统 NavigationBar 版本差异
1
2
3
4
// 大于等于 Android 6.0 版本的系统,如果背景是浅色的,可通过设置状态栏和导航栏文字颜色为深色,也就是导航栏和状态栏为浅色(Android 8.0 及以上才支持导航栏文字颜色修改)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR else 0
问题:在亮色系统 bar 基础上开启沉浸式后,在 8.0 至 9.0 系统中,导航栏深色导航 icon 不生效,而 10.0 以上版本能显示深色导航 icon:
通过查看源码发现,与设置状态栏和导航栏背景颜色类似,设置导航栏 icon 颜色也是不能为沉浸式:
与软键盘冲突的坑,输入框顶不起来(SystemUI Flag 引起的软键盘变化)
如果在界面中有 EditText 的话,你会发现当软件盘弹出的时候(Activity 已经设置了 adjustResize),ToolBar 的内容都被顶上去了,但是 EditText 输入框却被有顶上来(正常情况应该是 ToolBar 没事,输入框被软键盘顶上去)
在使用
SOFT_INPUT_ADJUST_RESIZE
,同时设置了View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
或View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
时,当键盘弹出时,只会 fitSystemWindow=true 的 view 所占区域会被 resize,其他 view 将会被软键盘覆盖。
华为 EMUI3.1 上的坑
没有沉浸式效果,状态栏是透明的,显示的是桌面上的颜色
Toast 错位(android:fitsSystemWindows)
不要在 Theme 中设置 android:fitsSystemWindows
。
Toast 打印出来的文字都往上偏移了,依附于 Application Window 的 Window(比如 Toast)错位
Toast 错位的解决方法如下:使用应用级别的上下文
1
Toast.makeText(getApplicationContext(),"toast sth...",Toast.LENGTH_SHORT).show();
Ref
- Android 沉浸式状态栏,看完这篇就够了!
https://blog.csdn.net/qq_34681580/article/details/103955191 - WindowInsetsController 简单使用
https://blog.csdn.net/jingzz1/article/details/111468726 - Android 系统 Bar 沉浸式完美兼容方案 (字节跳动)
https://juejin.cn/post/7075578574362640421