Compose动画 -- AnimatedVisibility的简单使用

678 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

概述

动画效果在应用中非常常见,我们通常使用动画来对用户进行一些提示,凸显重要信息的显示,或者在页面某一个状态变化之后通过动画通知用户,良好动画效果在现代的应用中是十分重要的,Compose已经向我们提供了一些动画API以及可以直接添加动画效果的可组合项,我们可以使用这些API和可组合项设置符合要求的动画效果。

AnimatedVisibility

这个可组合项可以为内容的显示和消失添加动画效果,我们只需要指定一些参数即可快速实现一个动画效果,如下面的代码所示:

                AnimatedVisibility(visible = showState.value) {
                    Surface(shape = CircleShape, color = Color.Blue, contentColor = Color.White) {
                        Text(
                            text = "演示\n动画",
                            Modifier
                                .size(80.dp)
                                .wrapContentSize(Alignment.Center),
                            textAlign = TextAlign.Center
                        )
                    }
                }

在上面的代码中,我们使用了AnimatedVisibility可组合项,其子项是一个文本框,我们希望它的子项在显示或者隐藏的时候能够使用动画,对于AnimatedVisibility,我们只是设置了它的visible属性,这表示其子项一开始是显示还是隐藏的状态,对于其子项的状态,我们这里通过remember来在当前可组合项中进行保存,当我们点击一个按钮的时候,子项的状态会发生改变。

运行上面的程序,可以看到如下的效果:

AnimatedVisibility简单使用

从上面的效果可以看出,AnimatedVisibility的子项在显示的时候从上方移动到下方,隐藏的时候从下方移动到上方,这是默认的动画效果。

进入到源码中,可以看到,默认情况下AnimatedVisibility执行的进入和退出的动画分别为"fadeIn() + expandVertically()"和"fadeOut() + shrinkVertically()",我们可以修改这两个参数来改变动画的效果。

下面的代码简单修改了子项进入和退出的动画,动画将会从左上角出现并在左上角消失,如下面的代码所示:

                AnimatedVisibility(visible = showState1, enter = expandIn(), exit = shrinkOut()) {
                    Surface(shape = CircleShape, color = Color.Blue, contentColor = Color.White) {
                        Text(
                            text = "演示\n动画",
                            Modifier
                                .size(80.dp)
                                .wrapContentSize(Alignment.Center),
                            textAlign = TextAlign.Center
                        )
                    }
                }

运行上面的代码,可以看到如下的效果:

AnimatedVisibility简单使用1

AnimatedVisibilityenter参数接收EnterTransition类型,exit参数则接收ExitTransition类型的动画,在Compose的代码中预定义了多种EnterTransitionExitTransition类型的动画,就像上面演示的都是Compose中预定义的。下面的表格中对预定义的各种类型的动画进行了演示。

进入动画退出动画动画效果
fadeIn()fadeOutfadein和fadeout
scaleIn()scaleOut()scalein和scaleut
slideInHorizontally()slideOutHorizontally()slideInHorizontally和slideOutHorizontally
slideInVertically()slideOutVertically()slidevertical
expandIn()shrinkOut()expandin和shrinkout
expandHorizontally()shrinkHorizontally()expandeHorizontal和shrinkHorizontal
expandVertically()shrinkVertically()expandVertical和shrinkVertical

从源码中的属性的默认值的设置可以看出,我们不仅可以指定单个动画,对于某一个状态的改变,进入和退出的时候我们都可以使用+连接多个动画,从而在进入或者退出的时候使用多个动画,比如下面的动画我们就指定了在向左移动的时候同时变小(退出),向右移动的时候同时变大(进入),如下代码所示:

                AnimatedVisibility(visible = showState1, enter = slideInHorizontally() + scaleIn(), exit = slideOutHorizontally() + scaleOut()) {
                    Surface(shape = CircleShape, color = Color.Blue, contentColor = Color.White) {
                        Text(
                            text = "演示\n动画",
                            Modifier
                                .size(80.dp)
                                .wrapContentSize(Alignment.Center),
                            textAlign = TextAlign.Center
                        )
                    }
                }

观察动画的状态

上面的动画必须要手动触发,如果我们希望动画能够在一开始进入可组合项的时候就执行,那么我们可以使用MutableTransitionState来定义状态,将它设置给visible属性即可在一开始就执行动画,另外这个对象还可以用来跟踪动画的状态。如下面的代码所示:

//定义状态
val showState2 = remember {
    MutableTransitionState(false).apply {
        this.targetState = true
    }
}

//可组合项
AnimatedVisibility(visibleState = showState2) {
    Surface(shape = CircleShape, color = Color.Blue, contentColor = Color.White) {
        Text(
            text = "演示\n动画",
            Modifier
                .size(80.dp)
                .wrapContentSize(Alignment.Center),
            textAlign = TextAlign.Center
        )
    }
}
//显示动画的状态
Text(
    text = when {
        showState2.isIdle && showState2.currentState -> "显示"
        showState2.isIdle && !showState2.currentState -> "隐藏"
        !showState2.isIdle && showState2.currentState -> "隐藏中"
        else -> "显示中"
    }
)

在上面的代码中,我们通过MutableTransitionState定义了动画的状态,初始状态为false,也就是说开始的时候子项没有显示出来,目标状态为true,即最终子项会通过动画显示出来,我们将这个状态设置给AnimatedVisibilityvisibleState就可以在一开始的时候执行动画了。

另外我们通过一个Text实时输出当前动画的执行状态,其实MutableTransitionState其中也就是三个状态,currentState表示当前的状态,如果子项已经显示则为true,否则为false。targetState表示的是目标状态,为true表示下一次将会显示子项,否则表示下一次将会隐藏子项。isIdle则表示是否转换完成,也就是动画是否执行完成。

运行上面的代码可以看到如下的效果:

获取动画执行的状态

为子项单独设置进入和退出的动画

通过AnimatedVisible设置的动画对于每一个子项都是相同的,有时候我们可能需要为不同的子项设置不同的显示和隐藏的动画。AnimatedVisible支持为其直接或者简介子项设置不同的动画,下面的代码实现了这个功能:

                AnimatedVisibility(
                    visibleState = showState3,
                    enter = EnterTransition.None,
                    exit = ExitTransition.None
                ) {
                    Row(Modifier.fillMaxWidth()) {
                        Surface(
                            shape = CircleShape,
                            color = Color.Blue,
                            contentColor = Color.White,
                            modifier = Modifier
                                .padding(end = 10.dp)
                                .animateEnterExit(
                                    enter = expandHorizontally(),
                                    exit = shrinkHorizontally()
                                )
                        ) {
                            Text(
                                text = "演示\n动画",
                                Modifier
                                    .size(80.dp)
                                    .wrapContentSize(Alignment.Center),
                                textAlign = TextAlign.Center
                            )
                        }
                        Surface(
                            shape = CircleShape,
                            color = Color.Blue,
                            contentColor = Color.White,
                            modifier = Modifier
                                .animateEnterExit(
                                    enter = slideInVertically(),
                                    exit = slideOutVertically()
                                )
                        ) {
                            Text(
                                text = "演示\n动画",
                                Modifier
                                    .size(80.dp)
                                    .wrapContentSize(Alignment.Center),
                                textAlign = TextAlign.Center
                            )
                        }
                    }
                }

在上面的代码中,我们给AnimatedVisible可组合项设置了显示和消失的动画均为None,这样AnimatedVisible就不会使用任何的动画,其子项完全使用通过Modifier.animateEnterExit指定的动画。

同时,上面的代码中我们给AnimatedVisible的两个间接子项设置了不同的动画,当状态改变的时候,子项会执行动画消失或者出现,可以对照上面的表格查看两个动画的效果。

运行上面的代码,可以看到如下效果:

为子项设置不同的动画