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

Compose LazyVerticalGrid切换跨度后滚动崩溃:IllegalStateException异常

解决Compose LazyVerticalGrid切换Item跨度后滚动崩溃问题

问题现象

在最新版Compose中,点击按钮切换列表项的跨度(从2改为1)后滚动应用,会触发如下崩溃:

FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 17202
java.lang.IllegalStateException: Place was called on a node which was placed already
at androidx.compose.ui.node.LayoutNode.onNodePlaced$ui_release(LayoutNode.kt:941)
at androidx.compose.ui.node.InnerNodeCoordinator.placeAt-f8xVGno(InnerNodeCoordinator.kt:137)
at androidx.compose.ui.layout.Placeable.access$placeAt-f8xVGno(Placeable.kt:35)
at androidx.compose.ui.layout.Placeable$PlacementScope.placeWithLayer-aW-9-wM(Placeable.kt:471)
...

复现代码

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun HomeScreen() {
    var spanNumber by remember {
        mutableStateOf(false)
    }
    val list = listOf(1,2,3,4,5,6,7,8,9,10)
    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        item(span = { GridItemSpan(2) }) {
            Button(onClick = { spanNumber = !spanNumber}) {
                Text(text = "Change mode")
            }
        }
        repeat(list.size) {
            item(span = { GridItemSpan(currentLineSpan =
            if (spanNumber) {
                2
            } else {
                1
            }
            ) }) {
                Column(verticalArrangement = Arrangement.Center) {
                    Text(text = "Hello $it", modifier = Modifier
                        .height(200.dp)
                        .padding(horizontal = 4.dp))
                }
            }
        }
    }
}

问题原因

动态修改LazyVerticalGrid中item的跨度时,Compose的LazyGrid复用机制会出现状态冲突:已存在的布局节点在跨度变化后,滚动触发视图复用时被重复调用place方法,导致节点状态异常,触发IllegalStateException。

解决方案

方案1:为动态跨度的Item添加唯一Key

给每个动态跨度的item设置唯一key,让Compose能够准确识别item的变化,避免复用错误的布局节点。修改后的代码如下:

@Composable
fun HomeScreen() {
    var spanNumber by remember {
        mutableStateOf(false)
    }
    val list = listOf(1,2,3,4,5,6,7,8,9,10)
    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        item(span = { GridItemSpan(2) }) {
            Button(onClick = { spanNumber = !spanNumber}) {
                Text(text = "Change mode")
            }
        }
        repeat(list.size) { index ->
            // 添加唯一key,确保Compose正确识别item
            item(key = index, span = { 
                GridItemSpan(currentLineSpan = if (spanNumber) 2 else 1) 
            }) {
                Column(verticalArrangement = Arrangement.Center) {
                    Text(text = "Hello $index", modifier = Modifier
                        .height(200.dp)
                        .padding(horizontal = 4.dp))
                }
            }
        }
    }
}

方案2:为LazyVerticalGrid添加依赖状态的Key

当跨度变化时,强制重建整个LazyVerticalGrid,避免节点复用问题。适合item数量较少的场景:

LazyVerticalGrid(
    // 当spanNumber变化时,重建整个Grid
    key = spanNumber,
    columns = GridCells.Fixed(2),
    verticalArrangement = Arrangement.spacedBy(8.dp)
) {
    // ... 原有内容
}

方案对比

  • 方案1更高效:仅更新状态变化的item,性能损耗小,适合大量item的场景。
  • 方案2实现简单:直接重建Grid,无需逐个处理item,但会有短暂的重建开销,适合小体量列表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 04:30:32