Compose中的可见性动画

7

我有一段需要动画显示或隐藏的文本,取决于值是否为空。如果能够单独处理可见性就会很简单,但是现在情况并非如此。

在下面的代码中,进入动画可以正常工作,但是当文本值为空时,退出动画无法正常工作。

我想到了一些方法,例如记住旧的值,但是不确定如何实现。

@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = text != null,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            text?.let {
                Text(text = it)
            }
        }
    }
}

“但是退出动画不起作用,因为文本值为空”这句话的意思是什么? - Gabriele Mariotti
它没有动画效果,只是消失了。 - SproutinGeek
3个回答

3

我认为淡出动画实际上是“本身”正常工作的。

我怀疑参数text:String?是来自上面某个地方提升的“状态”的值,因为您直接观察它位于动画范围内,当您将其更改为null时,它会立即删除Text组合,并且您不会见证缓慢的淡出。

AnimatedVisibility(
    ...
) {
    text?.let { // your'e directly observing a state over here
        Text(text = it)
    }
}

我根据自己的理解对你的片段进行了尝试并使其能够正常工作,淡入效果可以实现,但所需的淡出效果立即发生。

@Composable
fun SomeScreen() {

    var text by remember {
        mutableStateOf<String?>("Initial Value")
    }

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Button(onClick = {
            text = "New Value"
        }) {
            Text("Set New Value")
        }

        Button(onClick = {
            text = null
        }) {
            Text("Remove Value")
        }

        AnimatedText(text = text)
    }
}

@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = text != null,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            text?.let {
                Text(text = it)
            }
        }
    }
}

您可以通过将text修改为非状态值,并将可见性逻辑从使用可空性检查更改为某些需要它为visiblehidden的“业务逻辑”来解决此问题,如下所示修改上面的代码。

@Composable
fun SomeScreen() {

    var show by remember {
        mutableStateOf(true)
    }

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Button(onClick = {
            show = !show
        }) {
            Text("Set New Value")
        }

        AnimatedText(text = "Just A Value", show)
    }
}

@Composable
fun ShowAnimatedText(
    text : String?,
    show: Boolean
) {

    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = show,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {

            text?.let {
                Text(text = it)
            }
        }
    }
}

enter image description here


谢谢你的回答。如果我们尝试在ShowAnimatedText函数本身中记住旧文本,而不是从按钮点击发送占位符值,会怎样呢?因为我的屏幕不仅动画显示一个文本,而是一个更大的组合,带有一个相当大的对象作为参数。 - SproutinGeek
哦,那么只需通过这样做 val localText by remember {mutableStateOf(text)} 可能会解决你的问题。 - z.g.y

0

我通过记住前一个状态(或者不设置 null 值),直到退出动画完成,来解决了这个问题,如果文本是空的。

感谢 z.y 提供的建议。

@Composable
fun ShowAnimatedText(
    text : String?,
    show: Boolean
) {
    var localText by remember {
        mutableStateOf<String?>(null)
    }
    AnimatedContent(show, localText)
    LaunchedEffect(key1 = text, block = {
        if(text == null){
            delay(2000)
        }
        localText = text
    })
}
@Composable
private fun AnimatedContent(show: Boolean, localText: String?) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = show,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            localText?.let {
                Text(text = it)
            }
        }
    }
}

0
Jetpack Compose的AnimatedContent API可能是您想要的,因为您可以将数据对象传递给lambda函数并在那里检查其是否为null,例如:
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedContent(
            targetState = text,
            transitionSpec = {
                fadeIn(animationSpec = tween(2000)) with
                fadeOut(animationSpec = tween(2000))
            }
        ) { text ->
            text?.let {
                Text(text = it)
            }
        }
    }
}

  • 隐藏时没有任何动画故障(例如,当值变为null时)
  • 它也被父布局考虑在内(例如,兄弟视图平滑地动画以腾出空间)。

我遇到了同样的问题,这个答案帮助了我:https://dev59.com/oWwNtIcB2Jgan1znAryv#76307859


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接