如何在可空值情况下使用AnimatedVisibility?

3

我经常遇到这种情况。在下面的示例中,我有一个像plates这样的值,并且我想根据它是否为空来显示/隐藏它。但是,由于当其为null时不会渲染任何内容,因此隐藏它总是失败的,动画会立即变为空白无物。

如何才能使其正常工作?我想让plates一直存在,直到动画完成。

    AnimatedVisibility(
        visible = plates != null,
        content = {
            if (plates != null) {
                // Render plates
            } else {
                // The animation snaps to nothingness, as opposed to animating out
            }
        })

如果您想在动画的“退出”部分显示内容,则该内容不能为null。该内容必须与“进入”阶段执行时完全相同,否则动画会显示非常快的更改。为什么您的板变量突然变为null,而当“进入”阶段执行时它不是null? - Johann
恰好是我的想法。所以我应该记住内容,并在每次出现非空值时更新它,然后在animatedVisibility块中使用其值,同时通过最后一个值是否为空来指定其可见性?至于值再次变为null,这只是表明它不应再显示。 - zoltish
1
这是正确的解决方案。只需缓存您的最后一个项目,并在隐藏时重复使用它。此外,如果您的屏幕使用垂直滚动,则应将滚动状态放置在您的视图模型中,并将其传递给您的可组合项,因为动画会破坏滚动状态。使用像rememberScrollState这样的本地状态将无法正常工作。退出时不会跳回到正在退出的屏幕顶部,看起来更流畅。 - Johann
4个回答

6

这就是我最终做出的结果,它按预期工作!

@Composable
inline fun <T> AnimatedValueVisibility(
    value: T?,
    enter: EnterTransition = fadeIn(
        animationSpec = FloatSpec
    ) + expandVertically(
        animationSpec = SizeSpec,
        clip = false
    ),
    exit: ExitTransition = fadeOut(
        animationSpec = FloatSpec
    ) + shrinkVertically(
        animationSpec = SizeSpec,
        clip = false
    ),
    crossinline content: @Composable (T) -> Unit
) {
    val ref = remember {
        Ref<T>()
    }

    ref.value = value ?: ref.value

    AnimatedVisibility(
        visible = value != null,
        enter = enter,
        exit = exit,
        content = {
            ref.value?.let { value ->
                content(value)
            }
        }
    )
}

1
这就是我最终解决这个问题的方法。
/**
 * Wrapper around [AnimatedVisibility] that remembers the last state so that a nullable
 * state value can be used. Otherwise only the animate in works and the animate out is just
 * a quick flash.
 */
@Composable
inline fun <T> AnimatedNullableVisibility(
    value: T?,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandVertically(),
    exit: ExitTransition = fadeOut() + shrinkVertically(),
    crossinline content: @Composable (scope: AnimatedVisibilityScope, T) -> Unit
) {
    val ref = remember {
        Ref<T>()
    }

    ref.value = value ?: ref.value

    AnimatedVisibility(
        modifier = modifier,
        visible = value != null,
        enter = enter,
        exit = exit,
        content = {
            ref.value?.let { value ->
                content(this, value)
            }
        }
    )
}

1

我可能有点晚了,但是还有AnimatedContent可组合。它仍然需要选择加入ExperimentalAnimationApi,但它将通过其组合lambda参数向下传递模型,并且您可以检查其中一个是否为空。这是它的工作原理:

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable

@Composable
@OptIn(ExperimentalAnimationApi::class)
fun AnimatedPlates(plates: Plates?) {
    AnimatedContent(plates) { plates ->
        if (plates != null) {
            // Render plates
        } else {
            // Render either nothing, or some empty state.
        }
    }
}

希望这能帮到你!


-1

动画始终需要内容,即使没有要显示的内容。当内容为 null(或为空)时,只需显示一个 Surface:

AnimatedVisibility(
        visible = plates != null,
        content = {
            if (plates != null) {
                // Render plates
            } else {
                Surface(modifier = Modifier.fillMaxSize()) { }
            }
        })

1
这个具有相同的效果,当plates变为空时,空白的表面被渲染,看起来就像盘子立即消失了。 - zoltish

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