Jetpack Compose 动画:4 种技术让你的应用更具活力
Source: Dev.to
动画是现代 Android 应用的心跳。它将静态 UI 转化为流畅、响应式的体验,让用户感受到自然且愉悦的交互。Jetpack Compose——Google 为 Android 推出的现代声明式 UI 框架,提供了强大的内置 API,只需几行代码即可创建平滑的动画。
在本指南中,我将带你了解 Compose 中的四种关键动画技术,帮助你的应用从普通提升到卓越。每种技术都配有实用的代码示例,您可以立即在项目中使用。
Source: …
1. animateFloatAsState:平滑数值变化的基石
animateFloatAsState 可能是 Compose 中使用最广泛的动画 API。它能够将 float 值从当前状态平滑过渡到目标值,非常适合用于不透明度淡入淡出、缩放变化和旋转等场景。
使用案例:点击时按钮不透明度动画
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@Composable
fun AnimatedOpacityButton() {
var isPressed by remember { mutableStateOf(false) }
// Animate opacity from 1.0 to 0.5 when pressed
val alpha by animateFloatAsState(
targetValue = if (isPressed) 0.5f else 1.0f,
label = "button_alpha"
)
Button(
onClick = { isPressed = !isPressed },
modifier = Modifier.alpha(alpha)
) {
Text("Tap me")
}
}
为什么有效
- 平滑过渡 – 动画默认在 300 ms 内完成,营造自然的淡入淡出效果。
- 状态驱动 – 每当状态条件变化时,动画会自动触发。
- 性能优秀 – 使用底层图形 API,实现流畅的 60 fps 动画。
自定义动画速度
可以通过 animationSpec 来控制动画时长:
val alpha by animateFloatAsState(
targetValue = if (isPressed) 0.5f else 1.0f,
animationSpec = tween(durationMillis = 500), // 更慢的淡入淡出
label = "button_alpha"
)
2. AnimatedVisibility:带波兰语的显示/隐藏
虽然 animateFloatAsState 处理数值变化,AnimatedVisibility 则通过优雅的进入/退出动画将可组合项从视图层次结构中加入或移除。
使用场景:动画错误信息
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun LoginForm() {
var showError by remember { mutableStateOf(false) }
var email by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
TextField(
value = email,
onValueChange = { email = it },
label = { Text("Email") }
)
// 带滑动 + 渐变动画的错误信息
AnimatedVisibility(
visible = showError,
enter = slideInVertically() + fadeIn(),
exit = slideOutVertically() + fadeOut()
) {
Text(
"Invalid email format",
color = Color.Red,
modifier = Modifier
.background(Color(0xFFFFEBEE))
.padding(12.dp)
)
}
Button(onClick = {
showError = email.isEmpty() || !email.contains("@")
}) {
Text("Sign In")
}
}
}
动画组合
Compose 允许你组合多个动画:
enter = slideInVertically() + fadeIn() + expandVertically()
exit = slideOutVertically() + fadeOut() + shrinkVertically()
这样就能实现一种精致的效果:错误信息同时滑入、淡入并展开。
3. animateContentSize:平滑布局变化
当可组合项的大小因内容更新而改变时,animateContentSize 会对布局变化进行动画处理,而不是瞬间跳变。
使用场景:展开描述文本
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.tween
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@Composable
fun ExpandableText(title: String, fullText: String) {
var isExpanded by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.clickable { isExpanded = !isExpanded }
.animateContentSize(animationSpec = tween(durationMillis = 400))
.padding(16.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.headlineSmall
)
Text(
text = fullText,
maxLines = if (isExpanded) Int.MAX_VALUE else 3,
overflow = TextOverflow.Ellipsis
)
}
}
随意将这些代码片段复制到自己的项目中,并尝试调整参数,以匹配你的应用设计语言。
魔法
当 maxLines 从 3 更改为 MAX_VALUE 时,列的高度平滑展开。没有突兀的跳动——只有优雅的增长。
Source:
4. updateTransition + Crossfade:复杂状态动画
对于涉及多个属性同时变化的更复杂动画,updateTransition 将所有动画作为单一逻辑单元进行编排。
使用场景:带有旋转指示器的加载状态
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
enum class LoadingState {
Idle, Loading, Success, Error
}
@Composable
fun LoadingIndicator(state: LoadingState) {
val transition = updateTransition(targetState = state, label = "loading_transition")
val rotation by transition.animateFloat(label = "rotation") {
if (it == LoadingState.Loading) 360f else 0f
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Crossfade(targetState = state, label = "content_crossfade") { loadingState ->
when (loadingState) {
LoadingState.Idle -> Text("Ready to load")
LoadingState.Loading -> CircularProgressIndicator(
modifier = Modifier
.size(40.dp)
.rotate(rotation)
)
LoadingState.Success -> Text("Loaded!", color = Color.Green)
LoadingState.Error -> Text("Error occurred", color = Color.Red)
}
}
}
}
为什么 updateTransition 如此强大
- 编排 – 所有与同一状态变化绑定的动画同步进行。
- 一致性 – 防止动画相互冲突。
- 性能 – 仅在动画值变化时重新组合,而不是在每一帧都重新组合。
进阶:使用 Crossfade 实现优雅的内容切换
Crossfade 通过让旧的 composable 渐隐、新的 composable 渐显来实现两者之间的过渡。
@Composable
fun TabContent(selectedTab: Int) {
Crossfade(targetState = selectedTab, label = "tab_switch") { tab ->
when (tab) {
0 -> HomeScreen()
1 -> ProfileScreen()
else -> SettingsScreen()
}
}
}
无需编写复杂的进入/退出逻辑——Crossfade 会自动处理视觉上的优雅切换。
性能提示
- 使用
label参数 – 有助于在 Android Studio 的 Animation Inspector 中进行调试。 - 对简单数值使用
animateFloatAsState– 它经过优化且轻量。 - 避免在初始组合期间进行动画 – 动画开始前状态必须保持稳定。
- 在真实设备上测试 – 模拟器无法准确反映动画性能。
综合运用
最完善的应用会结合使用这些技术:
| Technique | Ideal Use |
|---|---|
animateFloatAsState | 快速状态反馈(按钮点击、切换) |
AnimatedVisibility | 进入/退出屏幕和对话框 |
animateContentSize | 内容自然增长(展开列表、文本) |
updateTransition | 复杂的同步状态变化 |
每种技术都针对特定的动画问题,掌握这四种技术后,你的 Compose 应用将带来真正令人愉悦的体验。
下一步
所有 8 个模板都使用干净的 Compose UI,已准备好进行动画:
首先在你的下一个功能中实现 animateFloatAsState,然后随着应用复杂度的提升,逐步探索其他技术。
祝动画愉快!