安卓15+Compose全面屏适配

111 阅读1小时+

随着Android 15(API 35)的发布,Google继续推动沉浸式全面屏UI设计。虽然这种设计提升了现代应用的视觉体验,但也引入了一些细微变化,可能会破坏现有布局——特别是如果你的应用依赖精确处理系统栏的话。本文将探讨如何为API 35迁移你的Jetpack Compose应用,并讨论管理全面屏UI过渡的最佳实践。

起点:当前设置

让我们看一个基本的Compose设置,包含白色工具栏和底部导航栏:

@Composable
fun MainScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(text = "全面屏示例") },
                actions = {
                    IconButton(onClick = { /* 处理设置 */ }) {
                        Icon(Icons.Default.Settings, contentDescription = "设置")
                    }
                },
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor = Color.White,
                    titleContentColor = Color.Black
                )
            )
        },
        bottomBar = {
            BottomAppBar(
                containerColor = Color.White,
                contentColor = Color.Black
            ) {
                IconButton(onClick = { /* 处理主页操作 */ }) {
                    Icon(Icons.Default.Home, contentDescription = "主页")
                }
                Spacer(modifier = Modifier.weight(1f))
                IconButton(onClick = { /* 处理搜索操作 */ }) {
                    Icon(Icons.Default.Search, contentDescription = "搜索")
                }
                Spacer(modifier = Modifier.weight(1f))
                IconButton(onClick = { /* 处理个人资料操作 */ }) {
                    Icon(Icons.Default.Person, contentDescription = "个人资料")
                }
            }
        },
        content = { innerPadding ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(innerPadding)
                    .background(Color.Gray),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = "Content goes here on API  ${Build.VERSION.SDK_INT}上",
                    fontSize = 22.sp
                )
            }
        }
    )
}

对应的运行效果如下。

image.png 可以看到,由于全面屏的变化,这个设置在API 34和API 35上的表现不同:

状态栏:

  • API 34:由于透明性,反映了灰色内容背景

  • API 35:默认为白色以保持一致性,但如果未配置则缺少可见图标

导航栏:

  • API 34:默认为黑色,因为没有显式配置

  • API 35:继承应用的白色BottomAppBar颜色以实现无缝外观

虽然这些变化符合现代设计标准,但它们需要调整以实现向后兼容性和视觉一致性。

解决方案:启用全面屏

要有效实现全面屏功能,你可以使用WindowCompat和WindowInsetsControllerCompat进行配置,示例代码如下。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 启用全面屏模式
        // 这会禁用默认系统行为,即窗口内容调整为适合系统栏(状态栏和导航栏)内部
        // 通过将此设置为false,允许应用内容绘制在系统栏下方,创建沉浸式全面屏体验
        // 然后你可以使用insets和修饰符管理内容如何与这些栏交互
        WindowCompat.setDecorFitsSystemWindows(window, false)
        
        // 调整状态栏和导航栏外观
        val insetsController = WindowInsetsControllerCompat(window, window.decorView)
        insetsController.isAppearanceLightStatusBars = true
        insetsController.isAppearanceLightNavigationBars = true
        
        // 可选:为状态栏和导航栏设置特定颜色
        window.statusBarColor = android.graphics.Color.TRANSPARENT
        window.navigationBarColor = android.graphics.Color.TRANSPARENT
        
        setContent {
            MainScreen()
        }
    }
}

再次运行项目,即可看到系统栏图标保持可见并与应用主题对齐。

image.png

高级配置:窗口Insets

为了在Jetpack Compose中精确控制insets,你可以使用内置修饰符如Modifier.windowInsetsPadding或Scaffold中的contentWindowInsets参数。这些工具允许你动态调整应用布局以适应系统栏区域,确保完美的全面屏体验。

Modifier.windowInsetsPadding

这个修饰符根据特定的系统insets(如状态栏、导航栏或系统手势)为你的可组合项添加填充。

Box(
    modifier = Modifier
        .fillMaxSize()
        .windowInsetsPadding(WindowInsets.systemBars)
) {
    Text("内容系统栏")
}

WindowInsets.systemBars的值会动态调整状态栏和导航栏的填充。

Scaffold中的contentWindowInsets

contentWindowInsets参数确保脚手架内容动态考虑系统insets以进行适当的布局调整。

Scaffold(
    contentWindowInsets = WindowInsets.systemBars,
    content = { innerPadding ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(innerPadding),
            contentAlignment = Alignment.Center
        ) {
            Text("系统栏填充的内容")
        }
    }
)

组合使用

对于复杂布局,可以将Modifier.windowInsetsPadding与其他布局修饰符结合使用来微调应用的UI。

Column(
    modifier = Modifier
        .fillMaxSize()
        .windowInsetsPadding(WindowInsets.systemBars)
) {
    Text("顶部内容")
    Spacer(modifier = Modifier.weight(1f))
    Text("底部内容")
}

通过利用上面的这些方式,可以创建一个响应式和沉浸式的UI,遵循全面屏原则而不影响可用性。