状态转移型动画 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. 在达到目标值后,动画结束。
相关推荐
alexhilton3 天前
Kotlin互斥锁(Mutex):协程的线程安全守护神
android·kotlin·android jetpack
是六一啊i4 天前
Compose 在Row、Column上使用focusRestorer修饰符失效原因
android jetpack
用户060905255226 天前
Compose 主题 MaterialTheme
android jetpack
用户060905255226 天前
Compose 简介和基础使用
android jetpack
用户060905255226 天前
Compose 重组优化
android jetpack
行墨6 天前
Jetpack Compose 深入浅出(一)——预览 @Preview
android jetpack
alexhilton7 天前
突破速度障碍:非阻塞启动画面如何将Android 应用启动时间缩短90%
android·kotlin·android jetpack
Pika8 天前
深入浅出 Compose 测量机制
android·android jetpack·composer
fundroid10 天前
掌握 Compose 性能优化三步法
android·android jetpack