对 BottomBar 的可见性进行动画处理会导致 Jetpack Compose 中的 ui 发生“跳跃”

m.r*_*ter 14 android android-jetpack-compose android-jetpack-compose-scaffold

我正在使用 jetpack compose 编写一个 android 应用程序。

这个应用程序有一个底部栏,我有时想使用动画隐藏它。然而,这证明是具有挑战性的:当我处理可滚动屏幕时,我的用户界面出现了一些“跳跃” - 请参阅帖子末尾。

我的最小示例如下所示:

@Preview
@Composable
fun JumpingBottomBarScreen() {
    var bottomBarVisible by remember { mutableStateOf(false) }

    Scaffold(
        content = { padding ->
            Column(
                modifier = Modifier
                    .verticalScroll(rememberScrollState())
                    .fillMaxWidth()
                    .background(Color.LightGray)
                    .padding(padding)
            ) {
                (1..20).forEach { Text(text = "Test #$it of 50") }

                Button(
                    onClick = { bottomBarVisible = !bottomBarVisible },
                    content = { Text(if (bottomBarVisible) "Hide Bottom Bar" else "Show Bottom Bar") }
                )

                (21..50).forEach { Text(text = "Test #$it of 50") }
            }
        },
        bottomBar = {
            AnimatedVisibility(
                visible = bottomBarVisible,
                enter = slideInVertically(initialOffsetY = { it }),
                exit = slideOutVertically(targetOffsetY = { it })
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(50.dp)
                        .background(Color.Red)
                )
            }
        }
    )
}
Run Code Online (Sandbox Code Playgroud)

避免AnimatedVisibility只支持偏移效果更好,但是,我只设法固定高度的底栏,这使得这种故障安全性大大降低。

bottomBar = {
    val bottomBarOffset by animateDpAsState(targetValue = if (bottomBarVisible) 0.dp else 50.dp)

    Box(
        modifier = Modifier
            .offset(y = bottomBarOffset)
            .fillMaxWidth()
            .height(50.dp)
            .background(Color.Red)
    )
}
Run Code Online (Sandbox Code Playgroud)

我该如何干净地做到这一点?我对屏幕底部的填充量比预期多感到满意。


左侧/顶部不好,右侧/底部良好(但高度固定)

坏的

好的

小智 1

您还需要为您的列填充设置动画:

@Composable
fun NotJumpingBottomBarScreen() {
    var bottomBarVisible by remember { mutableStateOf(false) }
    val bottomBarOffset by animateDpAsState(targetValue = if (bottomBarVisible) 0.dp else 50.dp)

    Scaffold(
        content = { padding ->
            Column(
                modifier = Modifier
                    .verticalScroll(rememberScrollState())
                    .fillMaxWidth()
                    .background(Color.LightGray)
                    .padding(
                        start = padding.calculateStartPadding(LocalLayoutDirection.current),
                        top = padding.calculateTopPadding(),
                        end = padding.calculateEndPadding(LocalLayoutDirection.current),
                        bottom = padding.calculateBottomPadding() - bottomBarOffset
                    )
            ) {
                (1..20).forEach { Text(text = "Test #$it of 50") }

                Button(
                    onClick = { bottomBarVisible = !bottomBarVisible },
                    content = { Text(if (bottomBarVisible) "Hide Bottom Bar" else "Show Bottom Bar") }
                )

                (21..50).forEach { Text(text = "Test #$it of 50") }
            }
        },
        bottomBar = {
            Box(
                modifier = Modifier
                    .offset(y = bottomBarOffset)
                    .fillMaxWidth()
                    .height(50.dp)
                    .offset(y = bottomBarOffset)
                    .background(Color.Red)
            )
        }
    )
}
Run Code Online (Sandbox Code Playgroud)