DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

LazyColumn 报 infinity maximum height

LazyColumn 报无限高度异常时,按整页滚动、固定页头或独立面板选择布局,统一 LazyColumn 内容,并沿父级排查无界测量约束。

给详情页外层 Column 加上 verticalScroll 后,页面可能在首次布局时直接崩溃,日志出现:

Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed.

常见结构是外层负责整页滚动,里面又放了一个没有指定高度的 LazyColumn。外层沿竖直方向给子内容提供无界高度,内层列表却需要有限的视口来决定显示哪些项,因此拒绝这次测量。

下文适用于 Jetpack Compose 1.x 的 Foundation 列表和布局 API,示例使用 Material 3。修复重点是父子布局传递的高度约束;增加 nestedScroll 回调或调整手势消费顺序不能直接解决这个异常。

标题、列表和按钮一起滚动时,用一个 LazyColumn

如果期望整个页面顺序滚动,把标题和页尾放进列表 DSL 的 item,中间数据交给 items。以下组件需要由具有有限高度的页面容器承载,例如 Activity 中正常占满窗口的 Compose 内容:

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

data class ProductRow(val id: Long, val name: String)

@Composable
fun ProductPage(
    products: List<ProductRow>,
    onAdd: () -> Unit,
    modifier: Modifier = Modifier
) {
    LazyColumn(
        modifier = modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp)
    ) {
        item(key = "page-title") {
            Text("商品清单", Modifier.padding(bottom = 16.dp))
        }
        items(products, key = { it.id }) { product ->
            Text(
                text = product.name,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 12.dp)
            )
        }
        item(key = "page-footer") {
            Button(onClick = onAdd) {
                Text("添加商品")
            }
        }
    }
}

移除这个列表外面的竖直滚动包装。fillMaxSize() 会使用父级可提供的有限空间,并不能在父级已经无界时创造一个可靠的窗口高度。

也不要为了规避嵌套,把全部商品放到一个 item { Column { ... } } 中。那样整个商品区会作为一个列表项一起布局,失去逐项按需组合的意义。多个分组可以在同一 LazyColumn 中交替使用分组标题 item 和数据 items。

标题固定、只有列表滚动时,外层不必滚动

如果页头必须留在屏幕上,使用普通 Column,让列表占剩余空间:

import androidx.compose.foundation.layout.Column

// 放在具有有限高度的 Compose 页面内。
Column(Modifier.fillMaxSize()) {
    Text("商品清单", Modifier.padding(16.dp))
    LazyColumn(Modifier.weight(1f).fillMaxWidth()) {
        items(products, key = { it.id }) { product ->
            Text(product.name, Modifier.padding(16.dp))
        }
    }
    Button(onClick = onAdd) {
        Text("添加商品")
    }
}

这里的 products、onAdd 与前一个组件参数含义相同。weight 是这个 Column 的作用域能力,前提是父布局有可分配的有限高度。保留外层 verticalScroll 再添加 weight(1f),不能照搬成通用修复。

页面确实需要两个滚动区域时,明确子区域高度

同方向嵌套并非一律禁止。详情页中如果需要一个独立滚动的短列表面板,可以给它明确的有限高度,例如:

import androidx.compose.foundation.layout.height

LazyColumn(Modifier.fillMaxWidth().height(240.dp)) {
    items(products, key = { it.id }) { product ->
        Text(product.name, Modifier.padding(16.dp))
    }
}

240.dp 只是面板尺寸示例,需按产品设计、字体大小和小屏可用空间选择。这样会形成内外两个滚动区域,应确认用户能理解和操作。不要给内层指定一个极大的高度来模拟“全部展开”;这可能增加测量工作,并让真实布局问题更难定位。

竖直列表中的水平 LazyRow 属于不同方向的组合,不应仅因看到嵌套列表就删除。仍要分别保证每个滚动方向上的测量边界合理。

没有外层 verticalScroll 时,沿父级继续查

错误中的无界高度也可能来自自定义 Layout、wrapContentSize(unbounded = true),或传统 View 与 ComposeView 的组合。先找抛错列表,再沿父级检查是谁在竖直方向放开了最大高度。可以在本地调试布局时查看相应位置收到的 Constraints.maxHeight,而不是不断给最内层加 fillMaxHeight()。

如果在 ScrollView 中嵌入 ComposeView,要重新决定滚动由哪一层负责:让外层内容提供有限区域,或把整页交给 Compose 的单个列表。改变滚动归属会影响滚动位置、固定按钮和可访问性导航,不能只以“异常消失”验收。

修复后检查空列表、长列表、横屏和大字体:页面应能首次打开,所有内容可以到达,页头页尾按设计固定或滚动。若回退结构,需同时回退相关高度和滚动修饰符,避免留下双重滚动。这个改动只调整呈现,不应修改商品数据或通过捕获异常跳过列表布局。