kotlin LazyColumn交换项动画

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

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

LazyColumn(
        contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),

    ) {
        items(
            items = leaderboard,
            key = { leaderBoard ->
                leaderBoard.rankingPlace
            }
        ) { leaderBoard ->
                RowComposable( modifier = Modifier
                    .fillMaxWidth(),
                    topicsItem = leaderBoard,)                
        }

字符串

xkftehaa

xkftehaa1#

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

class MyData(val uuid: String, val value: String)

val items: SnapshotStateList<MyData> = remember {
    mutableStateListOf<MyData>().apply {
        repeat(20) {
            add(MyData( uuid = UUID.randomUUID().toString(), "Row $it"))
        }
    }
}

字符串
2.函数来交换项

private fun swap(list: SnapshotStateList<MyData>, from: Int, to: Int) {
    val size = list.size
    if (from in 0 until size && to in 0 until size) {
        val temp = list[from]
        list[from] = list[to]
        list[to] = temp
    }
}


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

@Composable
private fun animatedSwap(
    lazyListState: LazyListState,
    items: SnapshotStateList<MyData>,
    from: Int,
    to: Int,
    onFinish: () -> Unit
) {

    LaunchedEffect(key1 = Unit) {

        val difference = from - to
        val increasing = difference < 0

        var currentValue: Int = from

        repeat(abs(difference)) {

            val temp = currentValue

            if (increasing) {
                currentValue++
            } else {
                currentValue--
            }

            swap(items, temp, currentValue)
            if (!increasing && currentValue == 0) {
                delay(300)
                lazyListState.scrollToItem(0)
            }
            delay(350)

        }
        onFinish()
    }
}


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

val lazyListState = rememberLazyListState()
LazyColumn(
    modifier = Modifier
        .fillMaxWidth()
        .weight(1f),
    state = lazyListState,
    contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
    verticalArrangement = Arrangement.spacedBy(4.dp)
) {
    items(
        items = items,
        key = {
            it.uuid
        }
    ) {
        Row(
            modifier = Modifier

                .animateItemPlacement(
                    tween(durationMillis = 200)
                )
                .shadow(1.dp, RoundedCornerShape(8.dp))
                .background(Color.White)
                .fillMaxWidth()
                .padding(8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Image(
                modifier = Modifier
                    .clip(RoundedCornerShape(10.dp))
                    .size(50.dp),
                painter = painterResource(id = R.drawable.landscape1),
                contentScale = ContentScale.FillBounds,
                contentDescription = null
            )
            Spacer(modifier = Modifier.width(10.dp))
            Text(it.value, fontSize = 18.sp)
        }
    }
}


演示

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun AnimatedList() {
    Column(modifier = Modifier.fillMaxSize()) {

        val items: SnapshotStateList<MyData> = remember {
            mutableStateListOf<MyData>().apply {
                repeat(20) {
                    add(MyData(uuid = UUID.randomUUID().toString(), "Row $it"))
                }
            }
        }

        val lazyListState = rememberLazyListState()

        LazyColumn(
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f),
            state = lazyListState,
            contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
            verticalArrangement = Arrangement.spacedBy(4.dp)
        ) {
            items(
                items = items,
                key = {
                    it.uuid
                }
            ) {
                Row(
                    modifier = Modifier

                        .animateItemPlacement(
                            tween(durationMillis = 200)
                        )
                        .shadow(1.dp, RoundedCornerShape(8.dp))
                        .background(Color.White)
                        .fillMaxWidth()
                        .padding(8.dp),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Image(
                        modifier = Modifier
                            .clip(RoundedCornerShape(10.dp))
                            .size(50.dp),
                        painter = painterResource(id = R.drawable.landscape1),
                        contentScale = ContentScale.FillBounds,
                        contentDescription = null
                    )
                    Spacer(modifier = Modifier.width(10.dp))
                    Text(it.value, fontSize = 18.sp)
                }
            }
        }

        var fromString by remember {
            mutableStateOf("7")
        }

        var toString by remember {
            mutableStateOf("3")
        }

        var animate by remember { mutableStateOf(false) }


        if (animate) {

            val from = try {
                Integer.parseInt(fromString)
            } catch (e: Exception) {
                0
            }

            val to = try {
                Integer.parseInt(toString)
            } catch (e: Exception) {
                0
            }

            animatedSwap(
                lazyListState = lazyListState,
                items = items,
                from = from,
                to = to
            ) {
                animate = false
            }
        }

        Row(modifier = Modifier.fillMaxWidth()) {

            TextField(
                value = fromString,
                onValueChange = {
                    fromString = it
                },
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
            )

            TextField(
                value = toString,
                onValueChange = {
                    toString = it
                },
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
            )

        }

        Button(
            modifier = Modifier
                .padding(8.dp)
                .fillMaxWidth(),
            onClick = {
                animate = true
            }
        ) {
            Text("Swap")
        }
    }
}

编辑:使用Animatable制作动画

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

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

val coroutineScope = rememberCoroutineScope()
val animatable = remember { Animatable(0, IntToVector) }


并且可以用作

private fun alternativeAnimate(
    from: Int,
    to: Int,
    coroutineScope: CoroutineScope,
    animatable: Animatable<Int, AnimationVector1D>,
    items: SnapshotStateList<MyData>
) {
    
    val difference = from - to
    var currentValue: Int = from

    coroutineScope.launch {
        animatable.snapTo(from)

        animatable.animateTo(to,
            tween(350 * abs(difference), easing = LinearEasing),
            block = {
                val nextValue = this.value
                if (abs(currentValue -nextValue) ==1) {
                    swap(items, currentValue, nextValue)
                    currentValue = nextValue
                }
            }
        )
    }
}


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

Button(
        modifier = Modifier
            .padding(8.dp)
            .fillMaxWidth(),
        onClick = {
            val from = try {
                Integer.parseInt(fromString)
            } catch (e: Exception) {
                0
            }

            val to = try {
                Integer.parseInt(toString)
            } catch (e: Exception) {
                0
            }
            alternativeAnimate(from, to, coroutineScope, animatable, items)
        }
    ) {
        Text("Swap")
    }


结果


的数据

34gzjxbg

34gzjxbg2#

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

data class Items(
   val otherItems: Other,
   val rankingCurrentPlace: Int?,
   val rankingLastPlace: Int?,
   val isUser: Boolean?
)

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

LazyColumn{
    items(list){
        (elements with condition)
    }
}

相关问题