状态转移型动画 AnimateXxxAsState()

传统View系统的属性动画

在了解Compose的动画之前,我们先来看看传统View系统中的属性动画是什么。

一个属性动画的简单示例:

java 复制代码
// 获取按钮组件
View view = findViewById(R.id.my_button);

// 创建动画,让按钮从0移动到300像素的位置(X轴)
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 300f);
animator.setDuration(1000); // 动画持续1秒

// 启动动画
animator.start();

在这个示例中,它可以让一个按钮:从左边移动到右边。

以上只是基本的使用,我们还可以添加:

  • 动画监听器(AnimatorListener) 来响应动画的不同阶段,比如在动画开始/结束/取消/重复 阶段,做一些操作。

  • 插值器(Interpolator) :控制动画的变化速度,它决定了动画在不同时间点的完成程度,我们可以让动画先加速后减速、先加速后加速、匀速,或者以弹跳曲线来变化速度。

  • 估值器(TypeEvaluator) :计算属性在起始值和结束值之间的实际数值,就是此次动画的变化量。

说了这么多,好像懂了?

不懂没关系,总结一下,属性动画过程:在指定的时间内,以帧为单位平滑地修改对象的属性值,从而实现流畅的动画效果。

如果用一张图来形象地这个过程就是:

而Compose是声明式UI框架,我们是描述各状态下的界面外观,所以不是去直接操作UI元素。

那该怎么办才能实现动画呢?你可能已经想到了,答案就在上一行,不断修改界面的描述状态就可以了。

是不是觉得很难,别担心,Compose官方已经为我们实现了一整套的动画API。

AnimateXxxAsState()

我们先来自己实现一个变化效果,点击绿色矩形,矩形会变大:

kotlin 复制代码
var size by remember { mutableStateOf(48.dp) }
Box(Modifier
    .size(size)
    .background(Color.Green)
    .clickable {
        size = 96.dp
    })

很明显,这种突变效果并不是真正意义上的动画。所以我们使用Compose为我们提供的animateDpAsState()函数来改造它:

kotlin 复制代码
var size by animateDpAsState(48.dp)
Box(Modifier
    .size(size)
    .background(Color.Green)
    .clickable {
        size = 96.dp
    })

animateDpAsState()函数具有重组时防止重复初始化创建一个可观察的状态对象的功能,所以只需一个函数就可以完成之前两个函数的工作。

可是修改后,发现报错了:

perl 复制代码
Type 'State<Dp>' has no method 'setValue(Nothing?, KProperty<*>, Dp)' and thus it cannot serve as a delegate for var (read-write property)

不对啊,你刚刚不是说animateDpAsState()函数可以替换掉前面的两个函数吗?怎么替换后,还错了。

这是因为之前的mutableStateOf()函数返回的是一个MutableState<Dp>类型的对象,可以对它的value属性进行读写;而animateDpAsState()函数返回的是一个State<Dp>类型的对象,它的value属性是只读的,所以我们不能进行修改。

那不能修改,动画还怎么进行下去啊,你不是就是要去不断地修改描述状态,才可以实现动画吗?

别急只是我们开发者不能修改,具体的修改过程由框架内部去完成。我们只需使用下面这种方式,就能完成动画了:

kotlin 复制代码
var big by remember { mutableStateOf(false) }
val size by animateDpAsState(targetValue = if (big) 96.dp else 48.dp)
Box(Modifier
    .size(size)
    .background(Color.Green)
    .clickable {
        big = !big
    })

这种方式来写的动画,代码非常简洁,我们只需声明目标状态应该是什么,不必关心状态的过渡细节。

注意: AnimateXxxAsState()是一个系列函数,其中还有animateIntAsState、animateOffsetAsState等函数,可以用于不同类型的动画过渡。

animateXxxAsState()的工作流程

  1. 创建AnimationState内部对象,并设置初始值和目标值。
kotlin 复制代码
val size by animateDpAsState(targetValue = if (big) 96.dp else 48.dp)
  1. 检测到targetValue目标值发生改变时,就开启一个动画。
kotlin 复制代码
// big值改变
var big by remember { mutableStateOf(false) }
  1. 动画运行期间,会在每一帧计算值,更新到AnimationState内部对象的value上,并且这个value值的更新会触发重组,导致使用到这个值的组件重组。
  2. 在达到目标值后,动画结束。
相关推荐
李斯维3 小时前
Jetpack 可观察数据容器 LiveData 的入门与基础使用
android·android jetpack
alexhilton4 小时前
车载系统中的可扩展UI:从UI嵌入到系统窗口编排
android·kotlin·android jetpack
帅次18 小时前
Android 17 开发者实战:核心更新与应用场景落地指南
android·java·ios·android studio·iphone·android jetpack·webview
我命由我123452 天前
Bugly - Bugly 基本使用( App 质量追踪平台)
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
帅次3 天前
Jetpack Compose 动画实战:animateFloatAsState、AnimatedVisibility 与 graphicsLayer 避坑
android·kotlin·gradle·android jetpack
帅次3 天前
Jetpack Compose 焦点与键盘:FocusRequester、imePadding 与 BringIntoView 实战
android·android studio·android jetpack·android runtime
黄林晴4 天前
Compose 架构大升级,终于支持列表项独立 ViewModel 了!
android·android jetpack
我命由我123455 天前
Dart - 数字类型、布尔类型、列表类型
android·开发语言·flutter·ios·uni-app·android jetpack·移动端
alexhilton8 天前
面向Android开发者的Google I/O 2026
android·kotlin·android jetpack
QING6189 天前
Kotlin inline 实战详解 —— 新手须知
android·kotlin·android jetpack