持续创作,加速成长!这是我参与「掘金日新计划 · 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
执行的进入和退出的动画分别为"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
的enter
参数接收EnterTransition
类型,exit
参数则接收ExitTransition
类型的动画,在Compose
的代码中预定义了多种EnterTransition
和ExitTransition
类型的动画,就像上面演示的都是Compose
中预定义的。下面的表格中对预定义的各种类型的动画进行了演示。
进入动画 | 退出动画 | 动画效果 |
---|---|---|
fadeIn() | fadeOut | |
scaleIn() | scaleOut() | |
slideInHorizontally() | slideOutHorizontally() | |
slideInVertically() | slideOutVertically() | |
expandIn() | shrinkOut() | |
expandHorizontally() | shrinkHorizontally() | |
expandVertically() | shrinkVertically() |
从源码中的属性的默认值的设置可以看出,我们不仅可以指定单个动画,对于某一个状态的改变,进入和退出的时候我们都可以使用+
连接多个动画,从而在进入或者退出的时候使用多个动画,比如下面的动画我们就指定了在向左移动的时候同时变小(退出),向右移动的时候同时变大(进入),如下代码所示:
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,即最终子项会通过动画显示出来,我们将这个状态设置给AnimatedVisibility
的visibleState
就可以在一开始的时候执行动画了。
另外我们通过一个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
的两个间接子项设置了不同的动画,当状态改变的时候,子项会执行动画消失或者出现,可以对照上面的表格查看两个动画的效果。
运行上面的代码,可以看到如下效果: