Android状态栏和导航栏非沉浸式实现方法

在Android应用开发中,特别是使用Jetpack Compose时,实现非沉浸式状态栏和导航栏(即保持它们的颜色与应用内容分离)需要一些特定的技术。本文档总结了在Jetpack Compose应用中实现黑色非沉浸式状态栏和导航栏的方法。

实现方法

1. 禁用默认的沉浸式行为

首先,需要禁用Android默认的沉浸式行为,这可以通过WindowCompat.setDecorFitsSystemWindows方法实现:

1
2
// 禁用沉浸式状态栏
WindowCompat.setDecorFitsSystemWindows(window, true)

这告诉系统窗口装饰(状态栏和导航栏)应该被考虑在内,内容不应该延伸到这些区域下方。

2. 控制状态栏和导航栏外观

在现代Android开发中,推荐使用WindowInsetsController来控制状态栏和导航栏的外观,而不是直接设置颜色:

1
2
3
4
5
6
// 获取窗口控制器
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)

// 设置为深色模式(深色背景,浅色图标)
windowInsetsController.isAppearanceLightStatusBars = false
windowInsetsController.isAppearanceLightNavigationBars = false

isAppearanceLightStatusBars = falseisAppearanceLightNavigationBars = false表示状态栏和导航栏使用深色背景,因此系统会使用浅色(白色)图标来确保可见性。

注意:旧的方式 window.statusBarColorwindow.navigationBarColor 已被弃用,不推荐使用。

3. 使用分层布局处理内容区域

为了确保内容不会被状态栏和导航栏遮盖,可以使用Compose的Box布局创建分层UI:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 使用Box布局,底层是黑色背景
Box(
    modifier = Modifier
        .fillMaxSize()
        .background(Color.Black)
) {
    // 上层是内容,添加padding确保不被状态栏和导航栏遮盖
    Surface(
        modifier = Modifier
            .fillMaxSize()
            .statusBarsPadding()
            .navigationBarsPadding(),
        color = MaterialTheme.colorScheme.background
    ) {
        // 内容放在这里
    }
}

这种方法创建了两层UI:

  • 底层是全屏的黑色背景,它会延伸到状态栏和导航栏下方
  • 上层是应用内容,使用statusBarsPadding()navigationBarsPadding()确保它不会被状态栏和导航栏遮盖

完整代码示例

以下是在Jetpack Compose应用中实现非沉浸式黑色状态栏和导航栏的完整代码示例:

 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
30
31
32
33
34
35
36
37
38
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 禁用沉浸式状态栏
        WindowCompat.setDecorFitsSystemWindows(window, true)
        
        // 获取窗口控制器来设置状态栏和导航栏外观
        val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
        
        // 设置状态栏和导航栏为深色模式(黑色背景,白色图标)
        // 这是现代Android推荐的方式,不再需要直接设置颜色
        windowInsetsController.isAppearanceLightStatusBars = false
        windowInsetsController.isAppearanceLightNavigationBars = false
        
        setContent {
            MyAppTheme {
                // 使用Box布局,底层是黑色背景
                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .background(Color.Black)
                ) {
                    // 上层是内容,添加padding确保不被状态栏和导航栏遮盖
                    Surface(
                        modifier = Modifier
                            .fillMaxSize()
                            .statusBarsPadding()
                            .navigationBarsPadding(),
                        color = MaterialTheme.colorScheme.background
                    ) {
                        // 应用内容
                    }
                }
            }
        }
    }
}

注意事项

  1. 现代化API:本文档使用的是现代Android推荐的API,避免了已弃用的方法如 window.statusBarColorwindow.navigationBarColor

  2. 兼容性:这些方法在Android 6.0(API级别23)及以上版本效果最佳。对于较旧的版本,可能需要使用其他方法。

  3. 主题设置:确保应用主题与这些设置兼容。例如,如果使用了深色主题,内容区域的背景颜色应该与状态栏和导航栏的深色形成良好的过渡。

  4. 测试:在不同设备上测试这些设置,特别是有刘海屏或打孔屏的设备,以确保视觉效果一致。

  5. 导航栏手势:在使用手势导航的设备上,导航栏可能不可见,但这些设置仍然会影响手势区域的背景颜色。

通过以上方法,可以实现状态栏和导航栏不沉浸的效果,保持它们的颜色(在本例中是黑色)与应用内容分离,提供更清晰的视觉边界。