状态转移型动画 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. 在达到目标值后,动画结束。
相关推荐
Lei活在当下1 天前
【业务场景架构实战】4. 支付状态分层流转的设计和实现
架构·android jetpack·响应式设计
天花板之恋2 天前
Compose之图片加载显示
android jetpack
消失的旧时光-19432 天前
Kotlinx.serialization 使用讲解
android·数据结构·android jetpack
Tans53 天前
Androidx Fragment 源码阅读笔记(下)
android jetpack·源码阅读
Lei活在当下4 天前
【业务场景架构实战】2. 对聚合支付 SDK 的封装
架构·android jetpack
Tans56 天前
Androidx Fragment 源码阅读笔记(上)
android jetpack·源码阅读
alexhilton7 天前
runBlocking实践:哪里该使用,哪里不该用
android·kotlin·android jetpack
Tans510 天前
Androidx Lifecycle 源码阅读笔记
android·android jetpack·源码阅读
ljt272496066111 天前
Compose笔记(四十九)--SwipeToDismiss
android·笔记·android jetpack
4z3313 天前
Jetpack Compose重组优化:机制剖析与性能提升策略
性能优化·android jetpack