android - 如何在 Jetpack Compose 中为动态列表的元素设置动画?
问题描述
如何使用动画将列表的元素转换为新列表(可能大小不同)?
我有一个饼图,当它的切片(分数)发生变化时,我想将以前的分数动画化为新的分数。问题是,切片的数量每次都可以不同。
如果新切片的数量小于当前切片的数量,则当前的额外切片应从其当前分数变为0
.
如果新切片的数量大于当前切片的数量,则新的额外切片应该从动画0
到它们的分数。
@Composable fun PieChartCompose(slices: List<Float>) {
val transitionData = updateTransitionData(slices)
val fractions = transitionData.fractions
// Draw the pie with Canvas using the fractions
}
我目前已经用一个恒定大小的列表(10,因此切片不能超过 10)实现了这一点
(请注意,图表外观的初始动画可能与后续动画不同):
data class TransitionData(val slices: List<State<Float>>)
enum class ChartState { INITIALIZED, CHANGED }
@Composable fun updateTransitionData(
targetFractions: List<Float>
): TransitionData {
val mutableState = remember { MutableTransitionState(ChartState.INITIALIZED) }
mutableState.targetState = ChartState.CHANGED
val transition = updateTransition(mutableState, label = "main-animation")
val fractions = listOf(
transition.animateFloat(label = "fraction-0-animation") {
if (it == ChartState.INITIALIZED) 0f
else targetSlices.getOrNull(0)?.fraction ?: 0f
},
// ...
transition.animateFloat(label = "fraction-10-animation") {
if (it == ChartState.INITIALIZED) 0f
else targetSlices.getOrNull(10)?.fraction ?: 0f
}
)
return remember(transition) { TransitionData(fractions) }
}
下面是一个示例图表,最初有两个切片,然后动画到一个切片
(第一个切片动画到单个新分数,第二个切片动画到0
-<br /> 它们有点不一致,可能是因为插值和动画规范) :
var slices by mutableStateOf(listOf(0.3f, 0.7f))
PieChartCompose(slices)
slices = listOf(1f)
解决方案
您可以尝试使用动态数量的animateFloat
.
由于我们想要为消失的分数设置动画,我们需要知道旧的分数列表(以防它比新的大)。
这就是为什么我将过渡状态更改为对分数列表进行操作。我们可以访问“旧”状态并找到“最大”大小(比较新旧分数列表大小)。
初始状态是一个空列表,因此最初会有从零开始的第一个分数的动画。
在animateFloat
我们尝试从目标中获取分数state
,如果该位置的分数不再存在 - 然后将其设为零,因此它将消失。
我还添加remember(values) { }
了更新animatedFractions
不需要工作的值,但它在那里而不是为了优化。如果计数values
不变,那么所有现有对象都将被重用,并且values
列表应该相同——那么我们不需要animatedFractions
使用新State
对象进行更新。
从updateTransitionData
一个稳定的对象返回,里面有一个稳定的列表。我们只修改该列表内的对象。因为它SnapshotStateList
会负责刷新所有Composables
迭代它的内容。
@Composable
fun updateTransitionData(
targetFractions: List<Float>
): TransitionData {
val mutableState = remember { MutableTransitionState(emptyList<Float>()) }
mutableState.targetState = targetFractions
val transition = updateTransition(mutableState, label = "main-animation")
val maxFractionsSize = max(transition.currentState.size, targetFractions.size)
val values = (0 until maxFractionsSize).map { index ->
transition.animateFloat(label = "fraction-$index-animation") { state ->
state.getOrNull(index) ?: 0f
}
}
val animatedFractions = remember(transition) { SnapshotStateList<State<Float>>() }
remember(values) {
animatedFractions.clear()
animatedFractions.addAll(values)
}
return remember(transition) { TransitionData(animatedFractions) }
}
推荐阅读
- dart - flutter - 如何保持用户登录谷歌
- php - 与在 PHP 中使用 use 函数直接调用类有什么区别?
- angular - 如何使用 Ngrx 创建嵌套状态结构?
- amazon-web-services - 如何获取/生成 aws quicksight 安全仪表板 URL
- java - 添加外部属性文件打包war Spring Boot
- ajax - Joomla Ajax 请求总是返回成功
- json - JSON 到 XML SXLT
- coordinates - AstroPy:如何将 GCRS 框架中的 SkyCoord 对象转换为地心框架?
- java - tomcat中简单的SESSION操作,内存使用量持续增长
- azure - 如何使用 REST API 找出租户名称