这篇文章介绍一些Compose官方已经封装好的,可以开箱即用的动画API。
animateContentSize内容大小动画
animateContentSize内容大小动画用于修改组件的内容大小时添加动画效果,常用场景为展示内容过多需要收起,点击后再将其显示完整。
fun Modifier.animateContentSize(
animationSpec: FiniteAnimationSpec<IntSize> = spring(),
finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
): Modifier = composed(
inspectorInfo = debugInspectorInfo {
name = "animateContentSize"
properties["animationSpec"] = animationSpec
properties["finishedListener"] = finishedListener
}
) {
val scope = rememberCoroutineScope()
val animModifier = remember(scope) {
SizeAnimationModifier(animationSpec, scope)
}
animModifier.listener = finishedListener
this.clipToBounds().then(animModifier)
}
animateContentSize为Modifier的扩展方法,因此只需要组件的modifier调用该方法,便可在该组件的内容进行变化重组的时候出现该动画。
animateContentSize内含两个参数,都有默认值,第一个参数为animationSpec动画规格,默认为SpringSpec弹性动画,可自定义动画效果,第二个参数为动画完成时的监听,默认为空。
简单示例:
var change by remember { mutableStateOf(false) }
Column(modifier = Modifier.animateContentSize()) {
Text(text = "点击改变内容大小", modifier = Modifier.clickable { change = !change })
if (change) {
Text(
text = "这是一个大方块",
modifier = Modifier
.size(100.dp)
.background(Color.Gray),
)
}
}
AnimatedVisibility可见性动画
AnimatedVisibility可见性动画用于组件的出现和消失时添加动画效果。
@Composable
fun AnimatedVisibility(
visible: Boolean,
modifier: Modifier = Modifier,
enter: EnterTransition = fadeIn() + expandIn(),
exit: ExitTransition = shrinkOut() + fadeOut(),
label: String = "AnimatedVisibility",
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visible, label)
AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content)
}
AnimatedVisibility有6个参数,只有第一个和最后一个参数必须填入,其余参数都有默认值。
- visible:定义内容是否应该可见,true 为可见,false 为不可见。
- modifier:修饰符,Compose里随处可见。
- enter:内容可见时的动画,默认为fadeIn() + expandIn(),大致意思为淡入并扩展开。
- exit:内容不可见时的动画,默认为shrinkOut() + fadeOut(),大致意思为缩小并淡出消失。
- label:标签,用于区分不同动画。
- content:添加可见性动画效果的组合项内容。
简单示例:
var change by remember { mutableStateOf(false) }
Column {
Text(text = "点击改变可见性", modifier = Modifier.clickable { change = !change })
AnimatedVisibility(visible = change) {
Text(
text = "这是一个大方块",
modifier = Modifier
.size(100.dp)
.background(Color.Gray),
)
}
}
当然可完全可以自定义可见/不可见时的动画效果:
var change by remember { mutableStateOf(false) }
Column {
Text(text = "点击改变可见性", modifier = Modifier.clickable { change = !change })
AnimatedVisibility(
visible = change,
enter = slideInVertically(
initialOffsetY = { fullHeight -> -fullHeight },
animationSpec = tween(durationMillis = 150, easing = LinearOutSlowInEasing)
),
exit = slideOutVertically(
targetOffsetY = { fullHeight -> -fullHeight },
animationSpec = tween(durationMillis = 250, easing = FastOutSlowInEasing)
)
) {
Text(
text = "这是一个大方块",
modifier = Modifier
.size(100.dp)
.background(Color.Gray),
)
}
}
animateValueAsState单一值属性动画
属性动画就是通过不断地修改属性值来实现的。animateValueAsState对任意值的属性动画。官方提供了一整套的api来实现简单的单一值属性动画。
image.png
实际使用的时候按需挑选即可。
@Composable
fun animateDpAsState(
targetValue: Dp,
animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
label: String = "DpAnimation",
finishedListener: ((Dp) -> Unit)? = null
): State<Dp> {
return animateValueAsState(
targetValue,
Dp.VectorConverter,
animationSpec,
label = label,
finishedListener = finishedListener
)
}
以animateDpAsState为例,共4个参数,第一个为目标值,必须填入,其余参数都有默认值,第二个参数为animationSpec动画规格,默认为SpringSpec弹性动画,可自定义动画效果,第三第四个参数前面出现过,按字面意思也可理解就不细说了。整个动画会以当前值为开始值,传入的targetValue为结束值,以animationSpec动画规格进行动画效果。
简单示例:
var small by remember { mutableStateOf(true) }
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改变方块大小")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
updateTransition多值动画
updateTransition可以保存一个或多个子动画,当状态发生改变时,多个动画同时进行。可以使用animate*扩展方法添加子动画。
简单示例:使用animateDp和 animateColor同时进行两个子动画
var change by remember { mutableStateOf(false) }
val transition = updateTransition(targetState = change, label = "多值动画")
val offset by transition.animateDp(label = "") { change ->
if (change) 50.dp else 0.dp
}
val background by transition.animateColor(label = "") { change ->
if (change) Color.Gray else Color.Blue
}
Column(modifier = Modifier.fillMaxWidth()) {
Text(text = "点击进行多个动画", modifier = Modifier.clickable { change = !change })
Text(
text = "这是一个大方块",
modifier = Modifier
.size(100.dp)
.offset(x = offset)
.background(background),
)
}
rememberInfiniteTransition无限重复动画
rememberInfiniteTransition可以进行无限重复动画,也一样可以保存一个或多个子动画,但是,这些动画一进入组合阶段就开始运行,除非被移除,否则不会停止,可以使用 animateColor、animatedFloat 或 animatedValue 添加子动画。
简单示例:使用animatedValue添加子动画改变大小size,并且使用animatedFloat添加子动画改变大小透明度alpha
val infiniteTransition = rememberInfiniteTransition()
val size by infiniteTransition.animateValue(
initialValue = 100.dp,
targetValue = 200.dp,
typeConverter = TwoWayConverter({ AnimationVector1D(it.value) }, { it.value.dp }),
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
val alpha by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse
)
)
Box(
Modifier
.size(size)
.padding(20.dp)
.alpha(alpha)
.background(Color.Red)
)
最后
最后,使用上面这些动画API可以达到一些简单的动画效果,但如果想要一些复杂的自定义的动画效果,可以使用animationSpec这个参数,具体见Compose动画学习之AnimationSpec。
网友评论