Compose系列之沉浸式标题栏edge-to-edge

一、前言

        Android原生开发到2025年需求已经是寥若星辰,AI的发展也是百花齐放,已经成为Coder的编码得力助手,尽管如此,我还是想把学到的东西消化一番写出来,即便Android开发在这个时代已经没落,想起复旦大学那句民间校训:“自由而无用”。
        搁笔三五载,这回有一些时间,业余把公司的项目用Compose写一遍,顺便分享一些开发心得。

二、edge-to-edge

        edge-to-edge英译汉就是我们app界面全屏效果的意思,也就是我们常说的沉浸式。在Android15之前,应用默认不是真正的全屏,因为我们APP界面默认不会越界到状态栏和底部栏的区域,而从15开始,则默认强制会延伸到状态栏和底部栏区域,但是兼容与美观程度考虑,我们强制enableEdgeToEdge

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
}

        我们应用的界面全屏了,但是可能有些组件,特别是需要用户点击的组件会被覆盖在状态栏或者底部栏,有两种办法解决这个问题,Inset size modifiers和 Padding Modifiers,这里的Inset通常指的就是状态栏、导航栏、软键盘这些系统层面的区域

1.使用占位(Inset size modifiers)

        前面说了,edgeToedge之后,我们应用全屏了,我们要避免statusBar和bottomBar覆盖了我们界面的一些按钮之类,我们可以写个占位组件(Spacer就挺好),正好替代这些栏的位置,但是我们不知道要占多少高度,我们需要知道statusBar和bottomBar的信息,而这些信息正是通过WindowInsets来提供,

状态栏:WindowInsets.statusBars
底部栏:WindowInsets.navigationBars

如果是想写个占位,比如状态栏我们想单独成白色,那么就可以写一个比如

Box(Modifier.windowInsetsTopHeight(WindowInsets.statusBars).fillMaxWidth().background(Color.White))

        通过windowInsetsTopHeight,把statusBars传进去,系统就知道我们这里是要占个·statusBars·的高度的位置,还有windowInsetsBottomHeight,相信你已经知道,就是底部导航栏的高度,把navigateBars传进去,底部占个导航栏位置,实际导航栏我们一般不去设置颜色,直接在根组件设置navigationBarsPadding也是可以。

2.使用Padding(Padding Modifiers)

        可以用Modifier.safeDrawingPadding()或者safeContentPadding,这样能够保证我们的应用不被遮挡,这种场景应该是不会常用,我们更多的还是想使用沉浸式标题栏,所以使用方法1可能更有效。在列表,比如LazyColumn中,我们可能想要列表能穿透systemBar,可以使用contentPadding = WindowInsets.statusBars.asPaddingValues()之类
        以下下这张图就可以很直观地感受到我们上面做的事

示意图

三、Inset consumption

        到此,我们已经实现了edge-to-edge,还有个概念,Inset consumption,实际上主要是配合软键盘的弹出与消失,首先还是按照原来的做法,在activity中设置android:windowSoftInputMode="adjustResize",接着就是在根组件设置Modifier.imePadding(),就是弹出键盘时,我们给键盘腾出padding,这样就不会被键盘遮挡住内容.

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Column(
        modifier = Modifier.fillMaxSize().systemBarsPadding().imePadding(),
    ) {
        LazyColumn(
            modifier = Modifier.weight(1f).imeNestedScroll(),
            reverseLayout = true
        ) {
            items(100) { index ->
                TextField(
                    modifier = Modifier.fillMaxWidth(),
                    value = index.toString(),
                    onValueChange = {  },
                )
            }
        }
    }
}

四、小结

        这么一小块内容,实际上花费的时间不少,还是没总结到位,但我想基本能满足一些开发需求,有问题我们评论区讨论,过后继续完善。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容