持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
概述
在上一篇笔记中,已经学习了使用AnimatedVisibility
在子项显示和隐藏的时候使用动画,这篇学习笔记将会学些其它一些动画的用法。
animateContentSize
animateContentSize
是一个修饰符,可以在可组合项的大小变化的时候使用相应的动画,如下面的代码所示:
Text(
text = inputContentState, modifier = Modifier
.background(Color.DarkGray)
.animateContentSize()
.padding(top = 10.dp)
)
在上面的代码中,我们对Text
可组合项指定了animateContentSize
修饰符,这样,当文本框中的内容变化引起尺寸变化的时候就能够看到相应的动画效果了。运行上面的程序,可以看到如下的效果:
从上面的动图可以看出,当Text的尺寸变化的时候使用的时动画。
Crossfade
这个可组合项用于在多个布局之间切换的时候使用动画,通过接收targetState
参数,可以使用淡入淡出动画切换内容,如下面的代码所示:
Crossfade(targetState = currentScreen.value) {
when (it) {
"A" -> Surface(
modifier = Modifier
.size(30.dp),
shape = CircleShape,
color = Color.Blue,
contentColor = Color.White
) {
Text(text = "A", modifier = Modifier.wrapContentSize(Alignment.Center))
}
"B" -> Surface(
modifier = Modifier
.size(40.dp),
shape = RoundedCornerShape(10.dp),
color = Color.Blue,
contentColor = Color.White
) {
Text(
text = "B",
textAlign = TextAlign.Center,
modifier = Modifier.wrapContentSize(
Alignment.Center
)
)
}
else -> Surface(
modifier = Modifier
.size(50.dp),
shape = RectangleShape,
color = Color.Blue,
contentColor = Color.White
) {
Text(text = "Other", Modifier.wrapContentSize(Alignment.Center))
}
}
}
在上面的代码中,我们定义的参数有三种状态,分别是"A","B","C",每种状态显示的大小和内容都是不一样的,当我们点击按钮改变状态的时候,Crossfade
会使用淡入淡出动画切换其子项,如下面的图片所示:
在之前的学习中,我们都是使用现有的可组合项或者修饰符对当前的可组合项或者其子项使用动画,我们可以指定动画类型但是无法指定动画的过程,很多时候系统提供的这些动画可能并不能满足我们的需求,我们需要能够更好的控制动画的实现,在Compose
中同样提供了一些较为底层的API来帮我们实现这样的需求。
animate[*]AsState
这个函数用于为单个值提供动画,我们只需要向其提供结束值,这个方法就会自动从当前值开始向目标值执行动画。这个函数是由Animatable
提供支持,Animatable
是一种基于协程的API,用于为单个值添加动画。
在下面的代码中,我们通过一个按钮来控制另一个可组合项的尺寸,点击按钮修改可组合项的尺寸,并且让这个尺寸以动画的形式变化,如下面的代码所示:
val width: Int by animateIntAsState(targetValue = if (scale) 120 else 60)
Surface(
modifier = Modifier.size(width.dp, 80.dp),
shape = RoundedCornerShape(10.dp),
color = Color.Blue,
contentColor = Color.White
) {
Text(
text = "演示\n动画",
modifier = Modifier.wrapContentSize(Alignment.Center),
)
}
在上面的代码中,我们对Surface
可组合项的宽度使用动画animateIntAsState
去定义,宽度大小会在120到60之间变化,如果当前已经是120了,那么就变化到60,否则变化到120.
运行上面的代码,可以看到如下的效果:
上面使用的是animateIntAsState
,在Compose
中为Float,Color,Dp,Size,Offset,Rect,Int,IntOffset和IntSize均提供了相应的animate[*]AsState
,我们可以在需要的地方直接使用。
Animatable
在上面一节我们已经提到了Animatable
,Animate[*]AsState
就是通过Animatable
实现的。这是一个值容器,在通过animateTo()
更改值时为值添加动画效果。Animatable
的许多功能都是使用挂起函数的形式实现,因此,它需要封装在适当的协程作用域内,我们可以使用LaunchedEffect
可组合项针对指定键值的时长创建一个作用域。如下面的代码所示:
val color = remember {
Animatable(Color.Green)
}
LaunchedEffect(isRed) {
color.animateTo(if (isRed) Color.Green else Color.Red)
}
Surface(
color = color.value,
contentColor = Color.White,
modifier = Modifier.size(80.dp),
shape = CircleShape
) {
Text(text = "演示\n动画", modifier = Modifier.wrapContentSize(Alignment.Center))
}
在上面的代码中,我们创建了一个Animatable
对象, 并传递颜色的初始值为绿色,接着获取到当前的协程作用域,在其中调用了改变颜色值的方法,然后创建了一个Surface
可组合项,设置其背景颜色为动画的当前值,通过改变isRed
的值,可以引起界面发生重组,重组的时候就可以看到可组合项背景的动画,如下面的图片显示:
需要注意的是:如果是Color
和Float
类型,则可以直接使用Animatable
,对于其它类型可以使用TwoWayConverter
转换之后即可使用。
AnimationSpec
在上面的代码中,我们一直在使用系统或者可组合项内部预定义的动画类型,我们可以看到它们能够实现什么样的效果,但是我们无法改变其参数,甚至我们没有改变动画执行的时间。通过使用AnimationSpec
参数,我们可以定义自己的动画行为,AnimationSpec
包含多种类型,我们可以根据具体的需求使用其中的某一种类型。
spring
这种类型可以在起始值和目标值之间使用基于物理特性的动画,也就是我们之前说过的弹性动画,这个类型可以接收dampingRatio
和stiffness
这两个参数,
其中dampingRatio
表示弹簧阻尼比,stiffness
则表示弹框的刚性。可以认为阻尼比表示的是动画回弹的次数,阻尼比越小,弹簧的会谈次数越多。而刚性则表示的是弹簧从开始位置到目标位置所需要的时间,刚性越大,需要的时间就越少,下面的代码使用弹性动画改变组合项的尺寸:
val size by animateDpAsState(
targetValue = if (scale) 100.dp else 50.dp,
animationSpec = spring(
dampingRatio = Spring.DampingRatioHighBouncy,
stiffness = Spring.StiffnessHigh
)
)
Surface(
color = Color.Blue,
contentColor = Color.White,
shape = CircleShape,
modifier = Modifier.size(size)
) {
Text(text = "演示\n动画", modifier = Modifier.wrapContentSize(Alignment.Center))
}
在上面的代码中,我们指定了动画的类型为弹性动画,其中阻尼比是较小的值,刚性设置的是较大的值,下面的表格中演示了使用不同值时动画的执行效果:
dampingRatio | stiffness | 执行效果 |
---|---|---|
DampingRatioHighBouncy | StiffnessHigh | |
DampingRatioHighBouncy | StiffnessMedium | |
DampingRatioHighBouncy | StiffnessMediumLow | |
DampingRatioHighBouncy | StiffnessLow | |
DampingRatioLowBouncy | StiffnessLow | |
DampingRatioLowBouncy | StiffnessMediumLow | |
DampingRatioLowBouncy | StiffnessMedium | |
DampingRatioLowBouncy | StiffnessHigh |
需要注意的是:DampingRatioHighBouncy
从数值上看其实是较小的值。从上面的动图中也可以看出:阻尼比越小,动画回弹的次数就越多,刚性越大,从起始值到目标值执行的速度就越快。
tween
这个动画类型可以指定动画的执行时间和曲线,使用这个动画类型通过durationMillis
参数指定动画的执行时间,通过easing
参数指定动画的执行曲线,也就是以什么样的曲线在起始值和目标值之间变化,同时还可以通过delayMillis
指定延迟多长时间启动动画,下面的代码演示了这个动画的使用方法:
val size by animateDpAsState(
targetValue = if (scale) 100.dp else 50.dp,
animationSpec = tween(durationMillis = 2000,easing = LinearOutSlowInEasing)
)
Surface(
color = Color.Blue,
contentColor = Color.White,
shape = CircleShape,
modifier = Modifier.size(size)
) {
Text(text = "演示\n动画", modifier = Modifier.wrapContentSize(Alignment.Center))
}
在上面的代码中我们指定了动画的执行时间为2秒,同时设置了执行曲线为LinearOutSlowInEasing
,运行上面的程序可以看到下面的效果: