You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Material3 Expressive与Navigation3实现透明导航栏的问题求助

Material3 Expressive与Navigation3实现透明导航栏的问题求助

各位大佬好,我现在在做一个单Activity架构的全Jetpack Compose项目,用的是Material3 Expressive组件(版本androidx.compose.material3:material3:1.5.0-alpha01)和最新的Navigation3库(androidx.navigation3:navigation3:1.0.0-SNAPSHOT),目标是实现透明导航栏——就是让页面内容能滑到系统导航按钮的下方,同时通过合适的内边距保证交互区域不被遮挡,类似很多主流App的效果。

目前状态栏已经完美实现了透明效果:内容可以延伸到状态栏下方,用Modifier.safeContentPadding()也能让组件自动避开状态栏区域,完全是预期的样子。但导航栏这边死活搞不定,试了各种方法都没达到想要的效果,想请大家帮忙排查下问题出在哪。

先贴下当前的核心配置代码

1. MainActivity的onCreate方法

我已经调用了官方推荐的enableEdgeToEdge(),配合启动页SplashScreen的设置:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    installSplashScreen().setKeepOnScreenCondition {
        return@setKeepOnScreenCondition mainViewModel.showSplashScreen
    }
    enableEdgeToEdge()
    setContent {
        PionTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                // 页面内容入口,嵌套NavDisplay
            }
        }
    }
}

2. 自定义Material3主题

主题配置比较简单,支持动态颜色适配:

@Composable
fun PionTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }
        darkTheme -> darkScheme
        else -> lightScheme
    }
    MaterialExpressiveTheme(
        colorScheme = colorScheme,
        typography = typography,
        content = content
    )
}

3. Manifest与XML主题配置

<application
    ...
    android:theme="@style/Theme.Pion">
    ...
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:theme="@style/Theme.App.Starting">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

对应的XML主题:

<style name="Theme.Pion" parent="android:Theme.Material.Light.NoActionBar"/>
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
    ...
    <item name="postSplashScreenTheme">@style/Theme.Pion</item>
</style>

遇到的具体问题

页面的核心结构是:NavDisplay中托管HomeScreenHomeScreen的根布局是Scaffold,底部栏用Surface包裹一个测试文本。

HomeScreen的简化代码:

Scaffold(
    modifier = Modifier.fillMaxSize(),
    bottomBar = {
        Surface(
            modifier = Modifier.fillMaxWidth(),
            color = MaterialTheme.colorScheme.secondaryContainer
        ){
            Text(
                text = "Test",
                modifier = Modifier
            )
        }
    }
) { paddingValues ->
    // 内部是LazyColumn,已经使用了Scaffold提供的paddingValues
    LazyColumn(contentPadding = paddingValues) {
        // 列表项
    }
}

我试了各种配置,结果都不符合预期:

  1. 不加safeContentPadding:底部Surface直接被系统导航栏覆盖,导航栏显示为半透明白色
  2. 给Surface加Modifier.safeContentPadding():Surface会上移避开导航栏,但导航栏依然是半透明白色,无法实现内容滑到导航栏下方的效果
  3. 只给Text加safeContentPadding():Text上移,但Surface仍卡在导航栏下方
  4. 去掉Scaffold的bottomBar:导航栏直接变成完全不透明的白色,LazyColumn的内容根本无法滑到导航栏后面,直接被截断

更诡异的是:如果给底部栏加一个垂直收缩的动画(比如滚动LazyColumn时缩小底部栏高度),动画过程中导航栏会短暂变成透明,但动画结束或底部栏完全显示/隐藏时,导航栏又立刻变回不透明白色。

已经尝试过的无效操作

  • 在所有XML主题中添加透明配置:
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:navigationBarColor">@android:color/transparent</item>
    
    状态栏本来就正常,导航栏完全没变化
  • 不想用WindowCompat.setDecorFitsSystemWindows(window, false)这类旧方法,因为官方文档明确说enableEdgeToEdge()已经处理了系统栏的透明和内容延伸逻辑,而且状态栏确实正常工作了,为什么导航栏会例外?

现在完全卡在这里了,想请教各位:是不是我对enableEdgeToEdge()safeContentPadding()的用法理解有误?还是Navigation3的NavDisplay有特殊的配置要求?或者Material3 Expressive主题有什么遗漏的设置?

麻烦大家帮忙看看,谢谢了!

火山引擎 最新活动