Анимация LaunchedEffect воспроизводится повторно для одного и того же компонуемого объекта

Я делаю приложение для игры в кости, в котором вы можете выбирать кости, чтобы добавить их в «поле». Когда кубик выбран, он появляется на экране и воспроизводится небольшая анимация покачивания, имитирующая его бросок на поверхность. Выбранные кубики отображаются в виде отсортированного вертикального списка. Проблема, с которой я столкнулся, заключается в том, что всякий раз, когда выбирается более одного кубика, анимацию всегда выполняет последний кубик в списке, а не только что выбранный кубик:

Похоже, что когда LazyColumn перестраивается, чтобы показать только что выбранный кубик, он считает, что последний кубик в списке всегда является новым, поэтому он каждый раз запускает свой блок LaunchedEffect. Вот как я делаю анимацию на кубике:

@Composable
fun DieIcon(
    die: Die,
    animated: Boolean
) {
    val interactionSource = remember { MutableInteractionSource() }
    var currentRotation by remember { mutableFloatStateOf(15f) }
    val rotation = remember { Animatable(currentRotation) }

    LaunchedEffect(Unit) {
        if (animated) {
            val result = rotation.animateTo(
                targetValue = -15f,
                animationSpec = repeatable(
                    iterations = 3,
                    animation = tween(
                        durationMillis = 50,
                        easing = LinearEasing
                    ),
                    repeatMode = RepeatMode.Reverse
                )
            ) {
                currentRotation = rotation.value
            }

            if (result.endReason == AnimationEndReason.Finished) {
                rotation.animateTo(
                    targetValue = 0f,
                    animationSpec = tween(
                        durationMillis = 200,
                        easing = LinearOutSlowInEasing
                    )
                ) {
                    currentRotation = rotation.value
                }
            }
        }
    }

    Box(
        contentAlignment = Alignment.Center,
        modifier = Modifier
            .rotate(
                if (animated) {
                    currentRotation
                } else {
                    0f
                }
            )
    ) {
        // show die image here
    }
}

И вот как я звоню DieIcon:

@Composable
private fun DiceList(
    dice: List<Die>,
    modifier: Modifier = Modifier
) {
    val state = rememberLazyListState()

    LaunchedEffect(dieUIModels.last()) {
        state.animateScrollToItem(dieUIModels.size)
    }

    LazyColumn(
        state = state,
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(8.dp),
        contentPadding = PaddingValues(vertical = 32.dp),
        modifier = modifier
            .fillMaxWidth()
            .padding(horizontal = 8.dp)
    ) {
        items(dice) { die->
            Box(
                contentAlignment = Alignment.Center
            ) {
                DieIcon(
                    die = die,
                    animated = true
                )
            }
        }
    }
}

Список dice сортируется в модели представления, а затем собирается как состояние в другом компонуемом объекте, который вызывает этот. Как я могу заставить анимацию запускаться каждый раз для вновь выбранного кубика?

1
0
111
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Согласно документации по предметам , есть второй параметр, который вы не используете — key:

фабрика стабильных и уникальных ключей, представляющих предмет. Использование одного и того же ключа для нескольких элементов списка не допускается. Тип ключа должен быть сохранен через Bundle на Android. Если передано значение null (по умолчанию), позиция в списке будет представлять ключ.

Итак, поскольку вы не переопределяете key, когда вы добавляете die в список, LazyColumn не знает, что вы добавляете его в начало списка - с его точки зрения, это единственный key, который был добавлен. был индексом позиции в конце списка.

Итак, вам нужен key, основанный на самом объекте die - таким образом, die в позиции 0 перемещается в позицию 1, когда вы добавляете новый die в начало списка. Таким образом, LazyColumn узнает, что первый элемент является новым, и правильно запустит его LaunchedEffect.

Другие вопросы по теме