kotlin LazyColumn交换项动画

ndh0cuux  于 2023-11-21  发布在  Kotlin
关注(0)|答案(2)|浏览(324)

我有一个10个项目的列表,其中一个有这个元素"rankingCurrentPlace""rankingPastPlace""isUser:true"
如果API响应像这样,我需要在lazycolumn上做一个动画"isUser:true""rankingPastPlace:3""rankingCurrentPlace:7"
我需要显示一个动画在列表中的行开始在第三位和下降到第七位
有没有办法做到这一点?
这是我真正拥有的

  1. LazyColumn(
  2. contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
  3. ) {
  4. items(
  5. items = leaderboard,
  6. key = { leaderBoard ->
  7. leaderBoard.rankingPlace
  8. }
  9. ) { leaderBoard ->
  10. RowComposable( modifier = Modifier
  11. .fillMaxWidth(),
  12. topicsItem = leaderBoard,)
  13. }

字符串

xkftehaa

xkftehaa1#

这个答案是有效的,除了当交换第一个项目与任何项目,即使没有动画的基本交换功能。我认为这将是更好地问一个新的问题,为什么交换第一个项目不工作,或者如果它是错误的。除此之外,工程预期。如果你需要移动到项目不在屏幕上,你可以lazyListState.layoutInfo.visibleItemsInfo和比较初始项目和滚动到它之前,动画
1.有一个SnapshotStateList的数据触发重组时,我们交换2个项目

  1. class MyData(val uuid: String, val value: String)
  2. val items: SnapshotStateList<MyData> = remember {
  3. mutableStateListOf<MyData>().apply {
  4. repeat(20) {
  5. add(MyData( uuid = UUID.randomUUID().toString(), "Row $it"))
  6. }
  7. }
  8. }

字符串
2.函数来交换项

  1. private fun swap(list: SnapshotStateList<MyData>, from: Int, to: Int) {
  2. val size = list.size
  3. if (from in 0 until size && to in 0 until size) {
  4. val temp = list[from]
  5. list[from] = list[to]
  6. list[to] = temp
  7. }
  8. }


3.一个接一个交换项目的函数。交换第一个项目有一个错误。即使它与上面的函数一起使用,当交换第一个项目时,其他项目也会向上移动,而不会通过Modififer.animateItemPlacement()显示动画。

  1. @Composable
  2. private fun animatedSwap(
  3. lazyListState: LazyListState,
  4. items: SnapshotStateList<MyData>,
  5. from: Int,
  6. to: Int,
  7. onFinish: () -> Unit
  8. ) {
  9. LaunchedEffect(key1 = Unit) {
  10. val difference = from - to
  11. val increasing = difference < 0
  12. var currentValue: Int = from
  13. repeat(abs(difference)) {
  14. val temp = currentValue
  15. if (increasing) {
  16. currentValue++
  17. } else {
  18. currentValue--
  19. }
  20. swap(items, temp, currentValue)
  21. if (!increasing && currentValue == 0) {
  22. delay(300)
  23. lazyListState.scrollToItem(0)
  24. }
  25. delay(350)
  26. }
  27. onFinish()
  28. }
  29. }


4.包含Modifier.animateItemPlacement()的项目的列表

  1. val lazyListState = rememberLazyListState()
  2. LazyColumn(
  3. modifier = Modifier
  4. .fillMaxWidth()
  5. .weight(1f),
  6. state = lazyListState,
  7. contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
  8. verticalArrangement = Arrangement.spacedBy(4.dp)
  9. ) {
  10. items(
  11. items = items,
  12. key = {
  13. it.uuid
  14. }
  15. ) {
  16. Row(
  17. modifier = Modifier
  18. .animateItemPlacement(
  19. tween(durationMillis = 200)
  20. )
  21. .shadow(1.dp, RoundedCornerShape(8.dp))
  22. .background(Color.White)
  23. .fillMaxWidth()
  24. .padding(8.dp),
  25. verticalAlignment = Alignment.CenterVertically
  26. ) {
  27. Image(
  28. modifier = Modifier
  29. .clip(RoundedCornerShape(10.dp))
  30. .size(50.dp),
  31. painter = painterResource(id = R.drawable.landscape1),
  32. contentScale = ContentScale.FillBounds,
  33. contentDescription = null
  34. )
  35. Spacer(modifier = Modifier.width(10.dp))
  36. Text(it.value, fontSize = 18.sp)
  37. }
  38. }
  39. }


演示

  1. @OptIn(ExperimentalFoundationApi::class)
  2. @Composable
  3. private fun AnimatedList() {
  4. Column(modifier = Modifier.fillMaxSize()) {
  5. val items: SnapshotStateList<MyData> = remember {
  6. mutableStateListOf<MyData>().apply {
  7. repeat(20) {
  8. add(MyData(uuid = UUID.randomUUID().toString(), "Row $it"))
  9. }
  10. }
  11. }
  12. val lazyListState = rememberLazyListState()
  13. LazyColumn(
  14. modifier = Modifier
  15. .fillMaxWidth()
  16. .weight(1f),
  17. state = lazyListState,
  18. contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
  19. verticalArrangement = Arrangement.spacedBy(4.dp)
  20. ) {
  21. items(
  22. items = items,
  23. key = {
  24. it.uuid
  25. }
  26. ) {
  27. Row(
  28. modifier = Modifier
  29. .animateItemPlacement(
  30. tween(durationMillis = 200)
  31. )
  32. .shadow(1.dp, RoundedCornerShape(8.dp))
  33. .background(Color.White)
  34. .fillMaxWidth()
  35. .padding(8.dp),
  36. verticalAlignment = Alignment.CenterVertically
  37. ) {
  38. Image(
  39. modifier = Modifier
  40. .clip(RoundedCornerShape(10.dp))
  41. .size(50.dp),
  42. painter = painterResource(id = R.drawable.landscape1),
  43. contentScale = ContentScale.FillBounds,
  44. contentDescription = null
  45. )
  46. Spacer(modifier = Modifier.width(10.dp))
  47. Text(it.value, fontSize = 18.sp)
  48. }
  49. }
  50. }
  51. var fromString by remember {
  52. mutableStateOf("7")
  53. }
  54. var toString by remember {
  55. mutableStateOf("3")
  56. }
  57. var animate by remember { mutableStateOf(false) }
  58. if (animate) {
  59. val from = try {
  60. Integer.parseInt(fromString)
  61. } catch (e: Exception) {
  62. 0
  63. }
  64. val to = try {
  65. Integer.parseInt(toString)
  66. } catch (e: Exception) {
  67. 0
  68. }
  69. animatedSwap(
  70. lazyListState = lazyListState,
  71. items = items,
  72. from = from,
  73. to = to
  74. ) {
  75. animate = false
  76. }
  77. }
  78. Row(modifier = Modifier.fillMaxWidth()) {
  79. TextField(
  80. value = fromString,
  81. onValueChange = {
  82. fromString = it
  83. },
  84. keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
  85. )
  86. TextField(
  87. value = toString,
  88. onValueChange = {
  89. toString = it
  90. },
  91. keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
  92. )
  93. }
  94. Button(
  95. modifier = Modifier
  96. .padding(8.dp)
  97. .fillMaxWidth(),
  98. onClick = {
  99. animate = true
  100. }
  101. ) {
  102. Text("Swap")
  103. }
  104. }
  105. }

编辑:使用Animatable制作动画

另一种制作动画的方法是使用AnimatableAnimatable矢量。

  1. val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
  2. TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })
  3. val coroutineScope = rememberCoroutineScope()
  4. val animatable = remember { Animatable(0, IntToVector) }


并且可以用作

  1. private fun alternativeAnimate(
  2. from: Int,
  3. to: Int,
  4. coroutineScope: CoroutineScope,
  5. animatable: Animatable<Int, AnimationVector1D>,
  6. items: SnapshotStateList<MyData>
  7. ) {
  8. val difference = from - to
  9. var currentValue: Int = from
  10. coroutineScope.launch {
  11. animatable.snapTo(from)
  12. animatable.animateTo(to,
  13. tween(350 * abs(difference), easing = LinearEasing),
  14. block = {
  15. val nextValue = this.value
  16. if (abs(currentValue -nextValue) ==1) {
  17. swap(items, currentValue, nextValue)
  18. currentValue = nextValue
  19. }
  20. }
  21. )
  22. }
  23. }


在按钮点击,我得到的值从文本字段我转换从字符串

  1. Button(
  2. modifier = Modifier
  3. .padding(8.dp)
  4. .fillMaxWidth(),
  5. onClick = {
  6. val from = try {
  7. Integer.parseInt(fromString)
  8. } catch (e: Exception) {
  9. 0
  10. }
  11. val to = try {
  12. Integer.parseInt(toString)
  13. } catch (e: Exception) {
  14. 0
  15. }
  16. alternativeAnimate(from, to, coroutineScope, animatable, items)
  17. }
  18. ) {
  19. Text("Swap")
  20. }


结果


的数据

展开查看全部
34gzjxbg

34gzjxbg2#

我建议你从一个数据类中获取你的项目。如果你的其他项目不包含你提到的变量,你可以在数据类中使它们为空,并在你的lazycolumn中放置一个条件检查器
这样

  1. data class Items(
  2. val otherItems: Other,
  3. val rankingCurrentPlace: Int?,
  4. val rankingLastPlace: Int?,
  5. val isUser: Boolean?
  6. )

字符串
然后可以从这个数据类中制作一个列表,并将其传递给LazyColumn

  1. LazyColumn{
  2. items(list){
  3. (elements with condition)
  4. }
  5. }

展开查看全部

相关问题