繁体   English   中英

如何在 Jetpack Compose 中为动态列表的元素设置动画?

[英]How to animate elements of a dynamic list in 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
由于插值和动画规范,它们有点不一致):

示例所需的动画

var slices by mutableStateOf(listOf(0.3f, 0.7f))
PieChartCompose(slices)
slices = listOf(1f)

您可以尝试使用动态数量的animateFloat

由于我们想要为消失的分数制作动画,我们需要知道旧的分数列表(以防它比新的大)。
这就是为什么我将转换状态更改为对分数列表进行操作的原因。 我们可以访问“旧”状态并找到“最大”大小(比较新旧分数列表大小)。
初始状态是一个空列表,因此最初的第一个分数将从零开始动画。

animateFloat我们尝试从目标state获取分数,如果该位置的分数不再存在 - 则将其设为零,这样它就会消失。

我还添加了remember(values) { }周围更新值animatedFractions它不需要工作,但它的存在而进行优化。 如果values的计数不会改变,那么所有现有对象都将被重用并且values列表应该相同——那么我们不需要用新的State对象更新animatedFractions

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) }
}

这是一个快速的“线性”演示,带有减慢的动画,通过 4 个不同的分数列表:
在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM