我们建议结合协程来管理你的自定义动画(Coroutine-based Animations)
Animation<T, V : AnimationVector>
这个接口,我们可以看下这张图Animation
接口的两个类,分别是 TargetBasedAnimation
和 DecayAnimation
,他们分别使用着独特的动画规范, AnimationSpec<T>
和 DecayAnimationSpec<T>
。TargetBasedAnimation
是为基于目标的动画服务的。Animatable.animateTo
来使用 TargetBasedAnimation
。DecayAnimation
直译为衰减动画。指的是随着时间的推移,初始设定的速度会逐渐变慢。DecayAnimation
的触发时机是没有作用的,建议直接使用高级 API 如 Animatable.animateDecay
等。Animatable.animateTo
, Animatable.animateDecay
将不详述。Animatable
是被设计成数据容器的类,用于保存目标数值。当动画执行的时候(如执行 animateTo
),它所持有的数据会根据设定进行改变;TargetBasedAnimation
会“继承”当前的数值和速度,而数值就是从 Animatable 对象中获取的;val alpha = remember { Animatable(0f) } // 指的是 alpha 是一个 Animatable 对象,它当前持有的数值为 0f
复制代码
// Animatable.kt
suspend fun animateTo(
targetValue: T,
animationSpec: AnimationSpec<T> = defaultSpringSpec,
initialVelocity: T = velocity,
block: (Animatable<T, V>.() -> Unit)? = null
): AnimationResult<T, V>
复制代码
animateTo
是一个挂起函数,也是 Animatable
对象的拓展函数;TargetBasedAnimation
的目标值、动画规范和初始速度;block
参数回调的是每一帧动画返回的 Animatable
对象,即我们可以在这里监听并获取动画的实时状态;AnimationResult
AnimationResult
包含两部分,一部分是动画的结束状态 endState
,包含了动画停止时最后一帧的状态(详见 AnimationState
);另一部分是动画的结束原因,包含正常结束或者是触达边界结束(详见 AnimationEndReason
)。SuspendAnimation.kt
中定义了如下方法:suspend fun animate(
initialValue: Float,
targetValue: Float,
initialVelocity: Float = 0f,
animationSpec: AnimationSpec<Float> = spring(),
block: (value: Float, velocity: Float) -> Unit
)
复制代码
animatable
变量,缺点是当前方法不会返回任何当前动画相关的属性;animateDecay
。onAnimationStart()
和 onAnimationEnd()
的监听器了,在动画的挂起函数里面是没有的。// 顺序动画
val scope = rememberCoroutineScope()
scope.launch {
animate(/*...*/) // 先执行 animationA
viewmodel.doSomething() // animationA 指定完成后执行 viewModel 逻辑
animate(/*...*/) // 等待 viewModel 执行完成后执行 animationB
}
复制代码
launch
创建新的协程来并发执行动画;// 并行动画
val scope = rememberCoroutineScope()
scope.launch {
launch {
animate(/*...*/) // 动画 A
}
launch {
animate(/*...*/) // 动画 B
}
}
// 上述写法,动画 A 和动画 B 会在同时执行。
复制代码
State<T>
来触发的,而动画也不例外。AnimationState<T, V>
,上面提到的返回最后一帧的动画状态也就是前面提到的这个类。而在动画的执行过程中,负责动画的挂起函数会持续发送新的 State 到 UI 上,具体可以看到这个方法 Animatable.runAnimation
// All the different types of animation code paths eventually converge to this method.
private suspend fun runAnimation(
animation: Animation<T, V>,
initialVelocity: T,
block: (Animatable<T, V>.() -> Unit)?
): AnimationResult<T, V> {
// Store the start time before it's reset during job cancellation.
val startTime = internalState.lastFrameTimeNanos
return mutatorMutex.mutate {
try {
// ...
endState.animate(
animation,
startTime
) {
updateState(internalState) // 关键逻辑
// ...
}
val endReason = if (clampingNeeded) BoundReached else Finished
endAnimation()
AnimationResult(endState, endReason)
} catch (e: CancellationException) {
endAnimation()
throw e
}
}
}
复制代码
Animation
、 Animatable
、 AnimationState
、animateTo
之间的关系;TargetBasedAnimation
和 DecayAnimation
两种;Animatable
中的挂起函数实现;AnimationState
触发数据刷新;完结撒花。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。