Jetpack Compose 动画实战:让你的 UI 动起来

传统的 Android View 动画体系(AlphaAnimation, ObjectAnimator, MotionLayout 等)往往割裂且复杂。而 Jetpack Compose 的动画 API 设计得非常直观声明式------你只需要描述"状态 A"和"状态 B",Compose 会自动帮你处理中间的过渡。

本文将由浅入深,带你掌握 Compose 动画的三板斧。

1. 入门:属性动画 (animate*AsState)

这是最简单、最常用的动画方式。当你只需要改变某个单一属性(如颜色、大小、透明度)时,使用它准没错。

场景:点击按钮,方块变大,颜色变红。

Kotlin 复制代码
@Composable
fun SimpleAnimationDemo() {
    // 1. 定义状态
    var isBig by remember { mutableStateOf(false) }

    // 2. 定义动画值 (Compose 会自动处理从当前值到目标值的平滑过渡)
    val size by animateDpAsState(
        targetValue = if (isBig) 100.dp else 50.dp,
        label = "size" // 用于 Layout Inspector 调试
    )
    val color by animateColorAsState(
        targetValue = if (isBig) Color.Red else Color.Blue,
        label = "color"
    )

    // 3. 使用动画值
    Box(
        modifier = Modifier
            .size(size)
            .background(color)
            .clickable { isBig = !isBig }
    )
}

常用的 API 有:

  • animateDpAsState:用于 dp 单位的值(如宽高、padding、offset)。
  • animateFloatAsState:用于浮点数值(如透明度 alpha、缩放 scale、旋转角度)。
  • animateColorAsState:用于颜色值(如背景色、文字颜色),会自动处理 RGB 的平滑过渡。
  • animateIntAsState:用于整数值(如列表索引、百分比进度)。

2. 进阶:可见性动画 (AnimatedVisibility)

在 View 体系中,隐藏/显示通常只是 View.GONE / View.VISIBLE 的生硬切换。Compose 提供了 AnimatedVisibility,让组件的出现和消失变得丝般顺滑。

场景:点击按钮,展开/收起一段文本。

Kotlin 复制代码
@Composable
fun VisibilityDemo() {
    var visible by remember { mutableStateOf(true) }

    Column {
        Button(onClick = { visible = !visible }) {
            Text(if (visible) "Hide" else "Show")
        }

        AnimatedVisibility(
            visible = visible,
            // 自定义进入动画:从顶部滑入 + 淡入
            enter = slideInVertically() + fadeIn(),
            // 自定义退出动画:向顶部滑出 + 淡出
            exit = slideOutVertically() + fadeOut()
        ) {
            // 这里放你想隐藏/显示的内容
            Text(
                text = "Peek-a-boo! I see you!",
                modifier = Modifier.padding(16.dp).background(Color.Yellow)
            )
        }
    }
}

Tips :你可以通过 + 号组合多个动画效果(如"淡入"+"放大")。


3. 高级:布局内容切换 (AnimatedContent)

当你需要根据状态完全替换 界面上的某个部分(例如:从"加载中"切换到"数据内容",或者数字的滚动变化)时,AnimatedContent 是神器。

场景:一个简单的计数器,数字变化时有类似老式翻页钟的上下滚动效果。

Kotlin 复制代码
@Composable
fun CounterDemo() {
    var count by remember { mutableIntStateOf(0) }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Button(onClick = { count++ }) { Text("Add") }

        AnimatedContent(
            targetState = count,
            transitionSpec = {
                // 定义:新数字从下往上进,旧数字从上往出
                // (slideInVertically { height -> height } + fadeIn())
                // .togetherWith(slideOutVertically { height -> -height } + fadeOut())
                
                // 简化写法:垂直滚动动画
                slideInVertically { it } togetherWith slideOutVertically { -it }
            },
            label = "counter"
        ) { targetCount ->
            // 这里不仅可以是 Text,也可以是复杂的 Composable
            Text(
                text = "$targetCount",
                style = MaterialTheme.typography.displayLarge
            )
        }
    }
}

4. 核心配置:动画规格 (AnimationSpec)

几乎所有的 Compose 动画 API 都有一个 animationSpec 参数,用于控制动画的速度曲线质感

A. 弹簧效果 (Spring) - 默认且推荐

如果你不设置,Compose 默认使用 spring()。它模拟物理世界的弹簧,没有固定的时长,只有阻尼和刚度,看起来最自然。

Kotlin 复制代码
animateDpAsState(
    targetValue = 100.dp,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioMediumBouncy, // 弹性系数 (高弹/低弹/无弹)
        stiffness = Spring.StiffnessLow // 刚度 (越低越慢且软)
    )
)

B. 补间动画 (Tween)

传统的基于时长的动画。

Kotlin 复制代码
animateDpAsState(
    targetValue = 100.dp,
    animationSpec = tween(
        durationMillis = 1000, // 1秒
        easing = LinearOutSlowInEasing // 缓动曲线
    )
)

C. 关键帧 (Keyframes)

用于精细控制动画过程中的每一个节点。

Kotlin 复制代码
animateDpAsState(
    targetValue = 100.dp,
    animationSpec = keyframes {
        durationMillis = 1000
        0.dp at 0 // 开始时 0dp
        50.dp at 200 // 200ms 时到达 50dp (快)
        100.dp at 1000 // 结束时 100dp
    }
)

5. 总结与建议

Compose 的动画系统非常庞大,但对于 90% 的日常开发,只需要记住以下决策路径:

  1. 只是改变某个属性值? -> 用 animate*AsState
  2. 让组件出现/消失? -> 用 AnimatedVisibility
  3. 两个组件互相切换? -> 用 AnimatedContent
相关推荐
千里马学框架15 小时前
如何改进车载三分屏SplitScreen启动交互方式?
android·智能手机·分屏·aaos·安卓framework开发·车载开发·3分屏
REDcker17 小时前
Android WebView 版本升级方案详解
android·音视频·实时音视频·webview·js·编解码
麦兜*17 小时前
【springboot】图文详解Spring Boot自动配置原理:为什么@SpringBootApplication是核心?
android·java·spring boot·spring·spring cloud·tomcat
le16161617 小时前
Android 依赖种类及区别:远程仓库依赖、打包依赖、模块依赖、本地仓库依赖
android
lxysbly17 小时前
psp模拟器安卓版带金手指
android
云上凯歌18 小时前
02 Spring Boot企业级配置详解
android·spring boot·后端
hqiangtai18 小时前
Android 高级专家技术能力图谱
android·职场和发展
aqi0018 小时前
FFmpeg开发笔记(九十七)国产的开源视频剪辑工具AndroidVideoEditor
android·ffmpeg·音视频·直播·流媒体
stevenzqzq19 小时前
Android Koin 注入入门教程
android·kotlin