AnimatedVisibility
、animateXxxAsState
、animateContentSize
、Crossfade
、AnimatedContent
这几个API,都是Compose
的高级别动画API
,是比较易用的。
上两篇文章我们已经介绍了AnimatedVisibility
,这篇文章我们会介绍剩下的那几个API
。
使用animateContentSize
,可让Compose
组件大小发生变化的时候,具备动画的效果。
比如,我们以下面这个例子为例
var message by remember { mutableStateOf("Hello") }
Column {
Button(onClick = {
message += "Hello"
}) {
Text(text = "Change")
}
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.background(Color.Blue)
.padding(10.dp)
.animateContentSize(),
contentAlignment = Alignment.Center
) {
Text(text = message, color = Color.White)
}
}
可以看到效果
当然,我们也可以给动画添加一些特效,比如回弹效果
var message by remember { mutableStateOf("Hello") }
Column {
Button(onClick = {
message += "Hello"
}) {
Text(text = "Change")
}
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.background(Color.Blue)
.padding(10.dp)
.animateContentSize(spring(Spring.DampingRatioHighBouncy)),
contentAlignment = Alignment.Center
) {
Text(text = message, color = Color.White)
}
}
关于回弹之类的animationSpec的动画效果,后续的文章会讲到,感兴趣的童鞋可以先关注一波。
注意:animateContentSize 在修饰符链中的位置顺序很重要。为了确保流畅的动画,请务必将其放置在任何大小修饰符(如 size 或 defaultMinSize)前面,以确保 animateContentSize 会将带动画效果的值的变化报告给布局。
animateEnterExit
需要使用在AnimatedVisibility
里面(直接或间接子项)。
我们可以使用Modifier.animateEnterExit
为每个子项指定不同的动画效果。
比如这里添加的slide
动画会覆盖AnimatedVisibility
设置的fade
动画。
var visible by remember {
mutableStateOf(false)
}
AnimatedVisibility(
visible = visible,
enter = fadeIn(tween(durationMillis = 2000)),
exit = fadeOut(tween(durationMillis = 2000))
) {
Box(
Modifier
.fillMaxSize()
.background(Color.Gray)
) {
Box(
Modifier
.align(Alignment.Center)
.animateEnterExit(
enter = slideInVertically(tween(durationMillis = 2000)),
exit = slideOutVertically(tween(durationMillis = 2000))
)
.sizeIn(minWidth = 64.dp, minHeight = 64.dp)
.background(Purple700)
) {
//Content of the notification...
}
}
}
LaunchedEffect(key1 = Unit, block = {
delay(2500)
visible = true
})
效果如下,这里动画的时长设为了2秒,可以看的更清楚一些
有时我们希望AnimatedVisibility
内部每个子组件的过渡动画各不相同,
那么就要为AnimatedVisibility
的enter
与exit
参数分别设置EnterTransition.None
和ExitTransition.None
,然后就可以在每个组件分别指定animateEnterExit
了。
animateEnterExit 显示是试验性质的,将来可能会发生变化,也可能会被完全移除。
Crossfade
用来将页面上显示的内容进行切换。
两个交替出现的组件进行渐变的切换,一个消失,而另一个显示出来。
动画效果是淡入淡出,并对尺寸进行处理 (瞬间改变)。
Crossfade
只支持这一种动画效果,是对于AnimatedContent
(我们下面会讲到) 的简化版本。
我们来试一下
Column(horizontalAlignment = Alignment.CenterHorizontally) {
var showPicture by remember { mutableStateOf(false) }
Crossfade(targetState = showPicture) {
if (it) {
Image(
painter = painterResource(id = R.mipmap.photot1),
modifier = Modifier.width(300.dp),
contentDescription = null
)
} else {
Box(
Modifier
.height(300.dp * 9 / 16)
.width(300.dp)
.background(Color.Blue)
)
}
}
Spacer(modifier = Modifier.height(10.dp))
Button(onClick = { showPicture = !showPicture }) {
Text(text = "切换")
}
}
显示效果
如果切换的两个组件,尺寸不一致,就会出现如下情况
如果转场前是大尺寸,转场后是小尺寸,会等转场快结束的时候,变成小尺寸
如果转场前是小尺寸,转场后是大尺寸,会在转场刚开始的时候,变成大尺寸
Column(horizontalAlignment = Alignment.CenterHorizontally) {
var showPicture by remember { mutableStateOf(false) }
Crossfade(targetState = showPicture) {
if (it) {
Image(
painter = painterResource(id = R.mipmap.photot1),
modifier = Modifier.width(300.dp),
contentDescription = null
)
} else {
Box(
Modifier
.size(60.dp)
.clip(RoundedCornerShape(30.dp))
.background(Color.Blue)
)
}
}
Spacer(modifier = Modifier.height(10.dp))
Button(onClick = { showPicture = !showPicture }) {
Text(text = "切换")
}
}
效果如下所示
AnimatedContent
用来控制多个组件的入场和出场,同时还能对入场和出场效果做定制
相当于是AnimatedVisibility
和Crossfade
的结合,AnimatedContent
出入场动画效果的尺寸是渐变的,这个是区别于Crossfade
的一个点。
AnimatedContent 显示是试验性质的,将来可能会发生变化,也可能会被完全移除。
Column(horizontalAlignment = Alignment.CenterHorizontally) {
var showPicture by remember { mutableStateOf(false) }
AnimatedContent(showPicture) {
if (it) {
Image(
painter = painterResource(id = R.mipmap.photot1),
modifier = Modifier.width(300.dp),
contentDescription = null
)
} else {
Box(
Modifier
.size(60.dp)
.clip(RoundedCornerShape(30.dp))
.background(Color.Blue)
)
}
}
Spacer(modifier = Modifier.height(10.dp))
Button(onClick = { showPicture = !showPicture }) {
Text(text = "切换")
}
}
显示效果如下所示
使用AnimatedContent
,设置自定义的动画效果,来实现数字增加的效果
Column {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }, Modifier.width(100.dp)) {
Text("增加")
}
Button(onClick = { count-- }, Modifier.width(100.dp)) {
Text("减少")
}
Box(
Modifier
.size(100.dp)
.border(1.dp, Color.LightGray)
.padding(5.dp)
.background(Color(0xFFF5F5F5)), contentAlignment = Alignment.Center
) {
AnimatedContent(targetState = count) { targetCount ->
Text(text = "$targetCount", fontSize = 20.sp)
}
}
}
我们给它设置一个自定义的动画效果
transitionSpec = {
if (targetState > initialState) {
slideInVertically { height -> height } + fadeIn() with
slideOutVertically { height -> -height } + fadeOut()
} else {
slideInVertically { height -> -height } + fadeIn() with
slideOutVertically { height -> height } + fadeOut()
}.using(
SizeTransform(clip = false)
)
}
Compose 动画系列,后续持续更新
Compose 动画 (一) : animateXxxAsState 实现放大/缩小/渐变等效果
Compose 动画 (二) : 为什么animateDpAsState要用val ? MutableState和State有什么区别 ?
Compose 动画 (三) : AnimatedVisibility 从入门到深入
Compose 动画 (四) : AnimatedVisibility 各种入场和出场动画效果