首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Jetpack Compose中动画动态列表的元素?

如何在Jetpack Compose中动画动态列表的元素?
EN

Stack Overflow用户
提问于 2021-10-12 09:29:03
回答 1查看 439关注 0票数 3

如何使用动画将列表元素转换为新列表(可能是不同大小的列表)?

我有一个饼图,当它的切片(分数)改变时,我想要将以前的分数动画化为新的分数。问题是,切片的数量每次都可能不同。

如果新切片的数量小于当前切片的数量,则当前的额外切片应从其当前分数设置为0动画。

如果新切片的数量大于当前切片的数量,则新的额外切片应从0到其分数设置动画。

代码语言:javascript
复制
@Composable fun PieChartCompose(slices: List<Float>) {
    val transitionData = updateTransitionData(slices)
    val fractions = transitionData.fractions
    // Draw the pie with Canvas using the fractions
}

我目前已经实现了一个固定大小的列表(10,所以切片不能超过10)

(请注意,图表外观的初始动画可以与后续动画不同):

代码语言:javascript
复制
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-

它们有点不一致,可能是因为插值和动画规格):

代码语言:javascript
复制
var slices by mutableStateOf(listOf(0.3f, 0.7f))
PieChartCompose(slices)
slices = listOf(1f)
EN

回答 1

Stack Overflow用户

发布于 2021-10-18 16:25:10

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

因为我们想要动画的分数消失了,我们需要知道旧的分数列表(以防它比新的大)。

这就是为什么我将转换状态改为对分数列表进行操作的原因。我们可以访问“旧”状态并找到“最大”大小(比较新旧分数列表大小)。

初始状态是一个空列表,因此最初将从零开始为第一个分数设置动画。

animateFloat中,我们尝试从目标state中提取分数,如果该位置的分数不再存在,则将其设为零,因此它将消失。

我还添加了关于更新animatedFractions中的值的remember(values) { },这不是工作所必需的,但它是为了优化而存在的。如果values的计数不会改变,那么所有现有的对象都将被重用,并且values列表应该是相同的-那么我们就不需要用新的State对象来更新animatedFractions

updateTransitionData返回一个稳定的对象,里面有一个稳定的列表。我们只修改该列表中的对象。因为它是一个SnapshotStateList,所以它将负责刷新遍历它的所有Composables

代码语言:javascript
复制
@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个不同的分数列表:

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69538128

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档