You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LazyVerticalGrid拖拽时所有Item同步移动问题求助

问题描述

我在Jetpack Compose的LazyVerticalGrid中为Card组件添加了pointerInput修饰符,原本预期每个列表项只响应自身的拖拽手势,但实际拖拽单个元素时,所有Item会同时移动。即便调用了change.consume(),手势仍会传递给所有元素。相关代码如下:

@Preview(showBackground = true)
@Composable
fun Greeting() {

    var initialXOffset by remember { mutableStateOf(0f) }
    var initialYOffset by remember { mutableStateOf(0f) }
    var xOffset by remember { mutableStateOf(0f) }
    var yOffset by remember { mutableStateOf(0f) }
    var dragging by remember { mutableStateOf(false) }
    val scale by animateFloatAsState(
        if (dragging) 0.9f else 1f, animationSpec = tween(
            durationMillis = 500,
            easing = LinearOutSlowInEasing
        )
    )

    LazyVerticalGrid(columns = GridCells.Fixed(3)) {
        val itemList = listOf<String>(
            "Viskin",
            "Emerys",
            "Zephrithe",
            "Milo",
            "Vamo",
            "Briu",
            "Ackner",
            "Wilo",
            "Ridner"
        )
        items(itemList.size) {
            Card(
                modifier = Modifier
                    .padding(8.dp)
                    .onGloballyPositioned { coordinates ->
                        initialXOffset = coordinates.positionInRoot().x
                        initialYOffset = coordinates.positionInRoot().y
                    }
                    .pointerInput(Unit) {
                        awaitPointerEventScope {
                            MainScope().launch {
                                detectDragGestures(
                                    onDragStart = {
                                        dragging = true
                                    },
                                    onDrag = { change, dragAmount ->
                                        change.consume()
                                        xOffset += dragAmount.x
                                        yOffset += dragAmount.y
                                    },
                                    onDragEnd = {
                                        dragging = false
                                    },
                                    onDragCancel = {
                                        dragging = false
                                    })
                            }
                        }
                    }
                    .scale(scale)
                    .graphicsLayer(translationX = xOffset, translationY = yOffset),
                shape = RoundedCornerShape(12.dp),
                elevation = CardDefaults.cardElevation(4.dp)
            ) {
                Text(
                    text = itemList[it],
                    modifier = Modifier
                        .padding(12.dp)
                        .align(CenterHorizontally),
                    fontSize = 12.sp,
                    fontWeight = FontWeight.Bold,
                    textAlign = TextAlign.Center
                )
            }
        }
    }
}

问题原因

所有列表项共享了同一组状态变量(xOffset、yOffset、dragging),只要这些变量发生变化,所有Card都会同步更新位置和缩放效果,这和手势是否被消费没有关系。

解决方案

为每个列表项单独维护拖拽相关状态,将状态逻辑封装到独立的Item组件中,确保每个Item拥有自己的偏移量和拖拽状态:

1. 封装独立的可拖拽Card组件

@Composable
fun DraggableCard(itemText: String) {
    // 每个Item单独维护自身的拖拽状态
    var xOffset by remember { mutableStateOf(0f) }
    var yOffset by remember { mutableStateOf(0f) }
    var dragging by remember { mutableStateOf(false) }
    val scale by animateFloatAsState(
        targetValue = if (dragging) 0.9f else 1f,
        animationSpec = tween(
            durationMillis = 500,
            easing = LinearOutSlowInEasing
        )
    )

    Card(
        modifier = Modifier
            .padding(8.dp)
            .pointerInput(Unit) {
                // 移除多余的MainScope.launch,detectDragGestures本身就在协程作用域内
                detectDragGestures(
                    onDragStart = { dragging = true },
                    onDrag = { change, dragAmount ->
                        change.consume()
                        xOffset += dragAmount.x
                        yOffset += dragAmount.y
                    },
                    onDragEnd = { dragging = false },
                    onDragCancel = { dragging = false }
                )
            }
            .scale(scale)
            .graphicsLayer(translationX = xOffset, translationY = yOffset),
        shape = RoundedCornerShape(12.dp),
        elevation = CardDefaults.cardElevation(4.dp)
    ) {
        Text(
            text = itemText,
            modifier = Modifier
                .padding(12.dp)
                .align(CenterHorizontally),
            fontSize = 12.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center
        )
    }
}

2. 修改原列表组件

@Preview(showBackground = true)
@Composable
fun Greeting() {
    LazyVerticalGrid(columns = GridCells.Fixed(3)) {
        val itemList = listOf(
            "Viskin", "Emerys", "Zephrithe",
            "Milo", "Vamo", "Briu",
            "Ackner", "Wilo", "Ridner"
        )
        items(itemList) { itemText ->
            DraggableCard(itemText = itemText)
        }
    }
}

补充说明

  • 移除了原代码中onGloballyPositioned的逻辑,若需要记录初始位置(比如实现拖拽复位),可在DraggableCard内部单独维护。
  • 去掉了多余的MainScope().launch,detectDragGestures本身运行在pointerInput的协程作用域中,无需额外启动协程。

内容的提问来源于stack exchange,提问作者OmniCoder77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:22:43