Android 状态栏、全屏与沉浸式状态栏详解
在 Android UI 里,"状态栏""全屏""沉浸式"常被混为一谈,但它们其实是三件不同的事,API 也分新旧两套。很多同学的沉浸式状态栏"在低版本好好的、高版本突然失效",多半就是没分清概念、又用错了过时 API。本文先把概念辨清,再分别讲全屏、透明状态栏、状态栏配色、WindowInsets、沉浸式模式,最后给出 AndroidX 现代统一方案。
1、概念辨析:状态栏、全屏、沉浸式
先分清三个常被混淆的概念,后面才不会用错 API:
状态栏(statusBar):屏幕顶部那条"时间/电量/通知图标"的条
导航栏(navigationBar):屏幕底部那条"返回/Home/最近"的条(部分机型是实体键则没有)
两者合称 systemBars(系统栏)
全屏(fullscreen):把 systemBars 隐藏掉,内容占满整块屏幕
沉浸式状态栏(透明状态栏):状态栏"仍然可见"但变透明,内容延伸到它下方
沉浸式模式(immersive mode):隐藏 systemBars 实现真·全屏,
用户从边缘滑入可临时唤出,松手又自动隐藏- "全屏/沉浸式模式"是"隐藏 systemBars";"沉浸式状态栏"是"状态栏透明可见"——方向相反,别搞混
- API 分两套:旧的 setSystemUiVisibility(已废弃,API 30 起 deprecated)、新的 WindowInsetsControllerCompat(AndroidX,推荐)
- 本文尽量用 AndroidX 新 API,兼容到老版本,避免"高版本失效"
2、全屏的三种方式
实现"Activity 全屏",主流有三种写法,按推荐度从新到旧:
// 方式一(推荐):WindowInsetsControllerCompat 隐藏 systemBars,API 30+/AndroidX
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.hide(WindowInsetsCompat.Type.systemBars()) // 隐藏状态栏+导航栏
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// 滑动会临时唤出、自动再隐藏(即 immersive 效果)
// 方式二(代码,老):FLAG_FULLSCREEN,API 30 起废弃
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
// 方式三(XML 主题):windowFullscreen=true,整 Activity 生效
// <style name="FullScreenTheme" parent="...">
// <item name="android:windowFullscreen">true</item>
// </style>
// 在 manifest 里给 Activity 用这个 theme选型建议:新项目一律用方式一(WindowInsetsControllerCompat),它是官方当前主推、兼容性好、且能同时控制"隐藏哪种 bars + 唤出行为";方式二是老写法,在 API 30+ 已废弃,过渡期还能用但不建议新写;方式三适合"整个 Activity 永远全屏"(如启动页、游戏页),用主题声明最干净。
3、沉浸式状态栏:让状态栏透明
所谓"沉浸式状态栏",准确说是"透明状态栏":状态栏不隐藏、依然显示时间电量,但背景透明,内容延伸到它下方——常见于"顶部图片顶到状态栏"的高级感效果。两步:把状态栏颜色设为透明 + 让内容布局延伸到状态栏区域:
// 第一步:状态栏背景透明
window.statusBarColor = Color.TRANSPARENT
// 第二步:内容延伸到状态栏区域(不要被它挤下去)
WindowCompat.setDecorFitsSystemWindows(window, false)
// 老写法等价:decorView.systemUiVisibility =
// View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE关键在第二步:默认情况下,DecorView 会"给内容预留"状态栏的高度(状态栏压在内容上方),设 `setDecorFitsSystemWindows(false)` 后,内容才会画到状态栏底下。但这样状态栏会遮住顶部内容——需要配合 fitsSystemWindows 或 WindowInsets 给顶部控件加状态栏高度的 padding(见第 5 节)。一个典型场景:背景图填满屏幕、Toolbar 透明、标题文字用 padding 顶到状态栏下方,就是"沉浸式状态栏"的视觉。
4、状态栏图标与文字颜色
状态栏透明后,新的问题来了:你的背景是浅色时,白色的状态栏图标(时间、电量)会"看不见";背景是深色时,黑色图标又看不清。所以要根据背景明暗切换状态栏图标的深浅色:
// 推荐(AndroidX):isAppearanceLightStatusBars
// true = 深色图标(适合浅色背景)
// false = 浅色图标(适合深色背景)
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.isAppearanceLightStatusBars = true // 深色图标
controller.isAppearanceLightStatusBars = false // 浅色图标
// 老写法:SYSTEM_UI_FLAG_LIGHT_STATUS_BAR(深色图标)
// decorView.systemUiVisibility = decorView.systemUiVisibility or
// View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR一条经验口诀:背景浅用深色图标、背景深用浅色图标,保证状态栏内容始终可读。沉浸式状态栏要"沉浸得好看",颜色配对和透明一样重要——很多"沉浸式翻车"的截图,都是浅色背景配了默认白色图标,时间电量糊成一团。
5、fitsSystemWindows 与 WindowInsets
透明状态栏让内容延伸到 systemBars 下方后,怎么避免内容被遮挡?靠的就是 WindowInsets 体系。两条路:
// 路线一:fitsSystemWindows=true(最省事)
// 给 Toolbar/根容器加 android:fitsSystemWindows="true",
// 系统会自动给它加上"状态栏高度的 paddingTop",把它顶到状态栏下方
// <androidx.appcompat.widget.Toolbar
// android:fitsSystemWindows="true" ... />
// 路线二(推荐,精确):ViewCompat.setOnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets ->
val statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
// 给 Toolbar 加状态栏高度的 padding,让标题不被状态栏遮挡
toolbar.updatePadding(top = statusBarHeight)
// 告诉系统我们消费了这些 insets
WindowInsetsCompat.CONSUMED
}fitsSystemWindows 是"自动挡":设 true 就自动避让,但它只对第一个设了它的父 View 生效、行为在某些嵌套结构里不直观。WindowInsetsListener 是"手动挡":你能精确拿到 statusBars/navigationBars 的具体尺寸,自己决定给谁加 padding。沉浸式状态栏的复杂布局,推荐用 WindowInsetsListener 精确控制,避免 fitsSystemWindows 在嵌套层级里行为不一致。
6、导航栏的透明与配色
底部导航栏和状态栏是"一对镜像",处理方式几乎一样:可以透明、可以配色、可以隐藏。
// 导航栏背景色(可设透明实现"沉浸到底部")
window.navigationBarColor = Color.TRANSPARENT
// 让内容延伸到导航栏下方
WindowCompat.setDecorFitsSystemWindows(window, false) // 同状态栏那一步
// 导航栏按钮配色(深/浅色图标,逻辑同状态栏)
val controller = WindowCompat.getInsetsController(window, window.decorView)
controller.isAppearanceLightNavigationBars = true // 深色按钮(浅色背景)
// 隐藏导航栏:和状态栏一样用 hide
controller.hide(WindowInsetsCompat.Type.navigationBars())注意导航栏的"按钮配色"只在 API 26(O)以上生效,更低版本无法改按钮颜色。要把整个 App 做成"上下都沉浸"(edge-to-edge),最干净的做法是第 8 节的 `enableEdgeToEdge()`,一键把状态栏、导航栏都设成透明 + 内容延伸。
7、Immersive Mode:真·沉浸式全屏
视频播放、游戏、阅读这类场景,要的是"连状态栏导航栏都隐藏,全屏沉浸",用户从边缘滑入能临时唤出——这就是 Immersive Mode。用 WindowInsetsControllerCompat 一行设行为:
val controller = WindowCompat.getInsetsController(window, window.decorView)
// 隐藏所有系统栏
controller.hide(WindowInsetsCompat.Type.systemBars())
// 关键:设置"滑动唤出后自动隐藏"的 transient 行为(immersive)
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
// 用户从边缘滑入 → systemBars 短暂出现 → 松手自动再隐藏
// 内容此时不会被挤动(因为是 transient 半透明叠加)对比老 API:过去的 `SYSTEM_UI_FLAG_IMMERSIVE`(滑出后不自动隐藏,要点一次才隐藏)与 `SYSTEM_UI_FLAG_IMMERSIVE_STICKY`(滑出后自动隐藏)两个标志,现在统一收敛到 `systemBarsBehavior` 一个属性。BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE 对应原来的 sticky,是"沉浸式 App"的标准选择。
8、AndroidX 现代统一方案:enableEdgeToEdge
新版 AndroidX 把这套"透明 + 延伸 + 配色"收敛成一个入口,强烈推荐新项目用它,省掉第 3~6 节的零碎设置:
// build.gradle 引入
// implementation "androidx.activity:activity-ktx:1.8+"
// implementation "androidx.core:core-ktx:1.12+"
// Activity 里一行开启 edge-to-edge(沉浸式)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() // 自动:状态栏/导航栏透明 + 内容延伸 + 配色适配明暗
setContentView(binding.root)
// 仍可用 WindowInsetsController 控制隐藏/immersive,用 setOnApplyWindowInsetsListener 处理 padding
}
}
// enableEdgeToEdge() 内部帮你做了:
// - DecorFitsSystemWindows = false(内容延伸)
// - 状态栏/导航栏颜色透明(或按系统主题给默认色)
// - 按系统深色/浅色模式自动设置图标深浅色
// 之后你只需在布局里用 WindowInsetsListener 给关键控件加 padding 即可统一原则:沉浸式/全屏的需求,能用 AndroidX(WindowCompat/WindowInsetsControllerCompat/enableEdgeToEdge)就别再用老的 setSystemUiVisibility 与 FLAG_*——前者向后兼容、API 30+ 仍是官方主推,后者已被废弃,在 targetSdk 较新时行为还会变化。把"透明靠 enableEdgeToEdge、隐藏靠 hide(systemBars)、配色靠 isAppearanceLight*、避让靠 WindowInsetsListener"这四件套用熟,状态栏相关需求基本全覆盖。
9、总结
状态栏、全屏、沉浸式是三件不同的事:"全屏/沉浸式模式"是隐藏 systemBars,"沉浸式状态栏"是状态栏透明可见、内容延伸到下方。实现上一律用 AndroidX 新 API——透明靠 enableEdgeToEdge/setDecorFitsSystemWindows(false),隐藏靠 WindowInsetsControllerCompat.hide,图标配色靠 isAppearanceLightStatusBars/NavigationBars,内容避让靠 fitsSystemWindows 或 WindowInsetsListener。
关键要点:
- 概念:全屏=隐藏 bars;透明状态栏=bars 可见但透明、内容延伸;immersive=隐藏且滑动唤出
- 全屏三法:WindowInsetsController.hide(systemBars)(新)/ FLAG_FULLSCREEN(旧废)/ windowFullscreen(theme)
- 透明状态栏:statusBarColor=TRANSPARENT + setDecorFitsSystemWindows(false)
- 图标配色:isAppearanceLightStatusBars 按背景明暗切换深/浅色图标
- 内容避让:fitsSystemWindows(自动)或 WindowInsetsListener(精确)
- 统一方案:enableEdgeToEdge + WindowInsetsControllerCompat,弃用 setSystemUiVisibility/FLAG_*
对于做 Android 界面的开发者而言,状态栏这套东西"看起来简单、坑却集中在版本差异和 API 废弃"。把"概念辨清 + 用 AndroidX 新 API + 四件套分工"这套方法论记住,再遇到"沉浸式状态栏图标看不清""内容被状态栏遮挡""全屏在 API 30 失效"这类问题,基本都能对号入座、一招解决。新项目直接 enableEdgeToEdge 起步,能把大部分状态栏烦恼消弭于无形。
学习资料:


评论 (1)
https://blog.csdn.net/ChrisSen/article/details/90241178