Я делаю приложение для игры в кости, в котором вы можете выбирать кости, чтобы добавить их в «поле». Когда кубик выбран, он появляется на экране и воспроизводится небольшая анимация покачивания, имитирующая его бросок на поверхность. Выбранные кубики отображаются в виде отсортированного вертикального списка. Проблема, с которой я столкнулся, заключается в том, что всякий раз, когда выбирается более одного кубика, анимацию всегда выполняет последний кубик в списке, а не только что выбранный кубик:
Похоже, что когда 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
сортируется в модели представления, а затем собирается как состояние в другом компонуемом объекте, который вызывает этот. Как я могу заставить анимацию запускаться каждый раз для вновь выбранного кубика?
Согласно документации по предметам , есть второй параметр, который вы не используете — key
:
фабрика стабильных и уникальных ключей, представляющих предмет. Использование одного и того же ключа для нескольких элементов списка не допускается. Тип ключа должен быть сохранен через Bundle на Android. Если передано значение null (по умолчанию), позиция в списке будет представлять ключ.
Итак, поскольку вы не переопределяете key
, когда вы добавляете die
в список, LazyColumn не знает, что вы добавляете его в начало списка - с его точки зрения, это единственный key
, который был добавлен. был индексом позиции в конце списка.
Итак, вам нужен key
, основанный на самом объекте die
- таким образом, die
в позиции 0 перемещается в позицию 1, когда вы добавляете новый die
в начало списка. Таким образом, LazyColumn узнает, что первый элемент является новым, и правильно запустит его LaunchedEffect
.