I have a custom Table layout that looks like the i...
# compose
m
I have a custom Table layout that looks like the image below, it measures & places cells based on the following policy: • For each row, all cells must have the same height, this height is the max cell height of the row's cells height • Similarly, For each column, all cells must have the same width, and this width is the max cell width of the column's cells widths I'm primarily using Intrinsics to query children sizes for every row or column without doing actual measurement to do the measurement later based on the calculated size. this works fine but as you can see in the image below as the table grows in size & more cells are added, the user should be able to scroll horizontally & vertically to see the rest of the table. but, when I try to add
verticalScroll
or
horizontalScroll
modifier to the Table layout, it results in infinite scrolling & eventually this exception is thrown. More in thread 🧵
1
Copy code
java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container.
                                                                                                    	at androidx.compose.foundation.CheckScrollableContainerConstraintsKt.checkScrollableContainerConstraints-K40F9xA(CheckScrollableContainerConstraints.kt:36)
                                                                                                    	at androidx.compose.foundation.ScrollingLayoutModifier.measure-3p2s80s(Scroll.kt:336)
I have tried to add
width(IntrinsicSize.Min)
/
height(IntrinsicSize.Min)
to the table layout modifier's chain in addition to adding the
horizontalScroll
&
verticalScroll
, but this results in a new exception being thrown,
Copy code
java.lang.IllegalArgumentException: Can't represent a size of 2147483647 in Constraints
	at androidx.compose.ui.unit.Constraints$Companion.bitsNeedForSize(Constraints.kt:403)
	at androidx.compose.ui.unit.Constraints$Companion.createConstraints-Zbe2FdA$ui_unit_release(Constraints.kt:366)
	at androidx.compose.ui.unit.Constraints$Companion.fixedWidth-OenEA2s(Constraints.kt:327)
	at androidx.compose.foundation.layout.MinIntrinsicWidthModifier.calculateContentConstraints-l58MMJ0(Intrinsic.kt:118)
	at androidx.compose.foundation.layout.IntrinsicSizeModifier.measure-3p2s80s(Intrinsic.kt:252)
My table layout code:
Copy code
@Composable
fun Table(
    contentRowsCount: Int,
    columnsCount: Int,
    modifier: Modifier = Modifier,
    headerRowContent: @Composable ((columnIndex: Int) -> Unit)? = null,
    cellContent: @Composable (rowIndex: Int, columnIndex: Int) -> Unit
) {
    val totalRowsCount = if (headerRowContent != null) contentRowsCount + 1 else contentRowsCount
    Layout(
        content = {
            repeat(totalRowsCount) { rowIndex ->
                repeat(columnsCount) { columnIndex ->
                    Cell(modifier = Modifier.cellLocation(rowIndex, columnIndex)) {
                        if (rowIndex == 0 && headerRowContent != null) {
                            headerRowContent(columnIndex)
                        } else {
                            cellContent(rowIndex, columnIndex)
                        }
                    }
                }
            }
        },
        modifier = modifier
//            .width(IntrinsicSize.Max)
//            .height(IntrinsicSize.Max)
//            .wrapContentSize()
//            .verticalScroll(rememberScrollState())
//            .horizontalScroll(rememberScrollState())
    ) { measurables, constraints ->
        val rowsHeights: Map<Int, Int> = measurables
            .groupBy { it.rowIndex!! }
            .mapValues { (_, rowMeasurables) -> rowMeasurables.maxOf { it.minIntrinsicHeight(constraints.maxWidth) } }

        val columnsWidths: Map<Int, Int> = measurables
            .groupBy { it.columnIndex!! }
            .mapValues { (_, columnMeasurables) -> columnMeasurables.maxOf { it.minIntrinsicWidth(constraints.maxHeight) } }

        val placeables = Array<Array<Placeable?>>(totalRowsCount) { Array(columnsCount) { null } }
        measurables.forEach {
            val rowIndex = it.rowIndex!!
            val columnIndex = it.columnIndex!!
            placeables[rowIndex][columnIndex] = it.measure(constraints = Constraints.fixed(width = columnsWidths[columnIndex]!!, height = rowsHeights[rowIndex]!!))
        }

        layout(constraints.maxWidth, constraints.maxHeight) {
            var x = 0
            var y = 0
            placeables.forEach { rowPlaceableColumns ->
                rowPlaceableColumns.forEach { placeable ->
                    placeable!!.placeRelative(x, y)
                    x += placeable.width
                }
                x = 0
                y += rowPlaceableColumns.first()!!.height
            }
        }
    }
}

@Composable
fun Cell(modifier: Modifier = Modifier, cellContent: @Composable BoxScope.() -> Unit) {
    Box(
        modifier = modifier.border(1.dp, Color.LightGray),
        content = cellContent,
        contentAlignment = Alignment.Center
    )
}
Apparently, the problem was that I'm using the passed in table constraints which were always infinite to layout cells regardless of the actual area/size cells occupy. So, the solution was simple, I replaced:
Copy code
layout(constraints.maxWidth, constraints.maxHeight) {
with the total columns widths/rows heights:
Copy code
layout(columnsWidths.map { (_, width) -> width }.sum(), rowsHeights.map { (_, height) -> height }.sum()) {
and the scrolling worked as intended!
439 Views