手把手教你写 Compose 动画 -- 过渡动画 API:Transition
📓 Transition
updateTransiton 是 Compose 中实现过渡动画的关键 API 。所谓过渡动画,即当依赖的某个状态发生改变时连锁发生的一系列动画效果。前面我们所提到的 Animate*AsState 与 Animatable 都是针对一个属性进行变换的,而 updateTransition 允许开发者将多个属性数值绑定到一个状态,当这个状态发生改变时,多个属性同时进行变换。
探索新技术的最佳方式是尝试它们,我们先构建一个简单场景:
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Column (modifier = Modifier.fillMaxWidth(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Spacer(modifier = Modifier.height(20.dp))Image(painter = painterResource(R.drawable.pingtouge),contentDescription = null,modifier = Modifier.size(90.dp).clip(shape = CircleShape).border(color = Color.Red, shape = CircleShape, width = 3.dp))Button(onClick = {}) {Text(text = "切换")}}}}
}
这段代码不难理解,效果如下:
现在我们假设一个需求场景:
- 图片大小 size 需要变化:小图片(90dp)、大图片(130dp)
- 图片边框颜色 color 需要变化:绿色、红色
- 对应关系:小图片绿色边框,大图片红色边框
现在我们开始讲解如何用 Transition 实现这个动画效果。
- 对于 updateTransition,状态可以是任何数据类型。我们自定义一个枚举类型:
private enum class ImageState {Small, Large
}
- 创建一个处理状态的变量:
var imageState by remember { mutableStateOf(ImageState.Small) }
- 创建 Transition 对象
我们先看下 updateTransition() 函数:
@Composable
fun <T> updateTransition(targetState: T,label: String? = null
): Transition<T>
它需要两个参数:
⇒ targetState:状态变量,当它被更改时,动画会进行。
⇒ label:动画的标签。
这里的状态就是我们之前定义的:imageState,所以我们可以像下面这样写:
val transition = updateTransition(targetState = imageState, label = "ImageState Transition")
updateTransition() 会返回一个 Transition 对象。
现在,我们可以使用 transition 对象来调用 animate* 函数。它们帮助我们为不同的 value 值制作动画。比如:
animateColor()、animateDp()、animateInt()、animateSize() 等。
- 定制边框颜色动画
val borderColor by transition.animateColor(label = "ImageState Color Transition") {when (it) {ImageState.Small -> Color.GreenImageState.Large -> Color.Magenta}
}
- 定制图片尺寸动画
val size by transition.animateDp(label = "ImageState Size Transition") {when (it) {ImageState.Small -> 90.dpImageState.Large -> 130.dp}
}
我们为每个属性状态(borderColor、size)声明了其在不同状态(ImageState.Small、ImageState.Large)时所对应的值,当过度动画所依赖状态(imageState)发生改变时,其中每个属性状态都会得到相应的更新。
- 应用到组件上(完整代码)
接下来,我们只需将创建的属性状态应用到我们的组件中即可:
private enum class ImageState {Small, Large
}class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {var imageState by remember { mutableStateOf(ImageState.Small) }val transition = updateTransition(targetState = imageState, label = "ImageState Transition")val borderColor by transition.animateColor(label = "ImageState Color Transition") {when (it) {ImageState.Small -> Color.GreenImageState.Large -> Color.Magenta}}val size by transition.animateDp(label = "ImageState Size Transition") {when (it) {ImageState.Small -> 90.dpImageState.Large -> 130.dp}}Column (modifier = Modifier.fillMaxWidth(),verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Spacer(modifier = Modifier.height(20.dp))Image(painter = painterResource(R.drawable.pingtouge),contentDescription = null,modifier = Modifier.size(size).clip(shape = CircleShape).border(color = borderColor, shape = CircleShape, width = 3.dp))Button(onClick = {imageState = if (imageState == ImageState.Small) {ImageState.Large} else {ImageState.Small}}) {Text(text = "切换")}}}}
}
效果如下: