1. 前言
我们首先来实现一个Compose的动画(animateDpAsState)
var big by remember {
mutableStateOf(false)
}
val size by animateDpAsState(if (big) 100.dp else 50.dp)
Box(
Modifier
.size(size)
.background(Color.Blue)
.clickable {
big = !big
}) {
}
运行程序,来看下效果

仔细看代码,我们可以发现,有这些疑问
- 为什么
animateDpAsState要用val? 而mutableStateOf就用var? MutableState和State有什么区别 ?- 为什么
animateDpAsState不需要包remember ? - 为什么把
mutableStateOf替换为animateDpAsState,就可以实现动画渐变的效果 ?
接下来,我们带着疑问就来看一些这几个疑问。
2. 为什么animateDpAsState要用val ?
我们分别点进animateDpAsState和mutableStateOf,发现他们的返回值有区别
animateDpAsState :
@Composable
fun animateDpAsState(
targetValue: Dp,
animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
finishedListener: ((Dp) -> Unit)? = null
): State<Dp> {
return animateValueAsState(
targetValue,
Dp.VectorConverter,
animationSpec,
finishedListener = finishedListener
)
}
mutableStateOf:
fun <T> mutableStateOf(
value: T,
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)
animateDpAsState返回值是State,而mutableStateOf的返回值是MutableState
正是State和MutableState的区别,导致animateDpAsState需要使用val,而mutableStateOf使用var
3. MutableState和State有什么区别 ?
那State和MutableState有什么区别呢 ?
我们查看源码可知
@Stable
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
@Stable
interface State<out T> {
val value: T
}
MutableState继承自State,都有value这个值。value这个值改变的时候,Compose会进行hook,从而通知界面改变的。
而MutableState和State的区别在于, State是只读的,而MutableState是可写可读的
从代码中我们也可以看出
State实现了kotlin委托机制中的getValue
inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
而MutableState才实现了kotlin委托机制中的setValue
inline operator fun <T> MutableState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
this.value = value
}
如果有童鞋不知道Kotlin委托的,可以看我的另一篇博客 看似普通的Android开发黑科技 - Kotlin 委托
所以animateValueAsState为什么要返回State呢 ? 相比答案也很明确了,因为动画是需要时间去过渡的,所以value的变化需要交给内部去处理,不能直接给value进行赋值,只能去指定目标值targetValue。
4. 为什么animateDpAsState不需要包remember ?
我们点击animateValueAsState,查看其内部源码,可以发现其内部已经包了remember,所以就不需要我们自己再调用remember了,但这也意味着animateXXXAsState只能在Composable函数内部调用了,可以看到代码中都有@Composable注解。
@Composable
fun <T, V : AnimationVector> animateValueAsState(
targetValue: T,
typeConverter: TwoWayConverter<T, V>,
animationSpec: AnimationSpec<T> = remember {
spring(visibilityThreshold = visibilityThreshold)
},
visibilityThreshold: T? = null,
finishedListener: ((T) -> Unit)? = null
): State<T>
5. 为什么把mutableStateOf替换为animateDpAsState,就可以实现动画渐变的效果 ?
到这里,我们想必已经明白了,animateDpAsState的返回值是State,其内部的value只能通过内部改变,从而更新Compose界面,animateDpAsState内部已经帮我们处理好动画渐变值平滑的过度了。
@Composable
fun <T, V : AnimationVector> animateValueAsState(
targetValue: T,
typeConverter: TwoWayConverter<T, V>,
animationSpec: AnimationSpec<T> = remember {
spring(visibilityThreshold = visibilityThreshold)
},
visibilityThreshold: T? = null,
finishedListener: ((T) -> Unit)? = null
): State<T> {
val animatable = remember { Animatable(targetValue, typeConverter) }
val listener by rememberUpdatedState(finishedListener)
val animSpec by rememberUpdatedState(animationSpec)
val channel = remember { Channel<T>(Channel.CONFLATED) }
SideEffect {
channel.trySend(targetValue)
}
LaunchedEffect(channel) { //Compose中的协程
for (target in channel) {
// This additional poll is needed because when the channel suspends on receive and
// two values are produced before consumers' dispatcher resumes, only the first value
// will be received.
// It may not be an issue elsewhere, but in animation we want to avoid being one
// frame late.
val newTarget = channel.tryReceive().getOrNull() ?: target
launch {
if (newTarget != animatable.targetValue) {
animatable.animateTo(newTarget, animSpec) //平滑过度到目标值
listener?.invoke(animatable.value)
}
}
}
}
return animatable.asState()
}


















