https://kotlinlang.org logo
#compose
Title
# compose
m

Marco Pierucci

11/03/2022, 4:48 PM
Hello! I'm having issues with a custom Table composable that exposes a slot for cell content. I can measure the cell space and account to the biggest size, but cant make cells with less size to fill the slot ( as shown in the picture) snippet on 🧵
Copy code
@Composable
fun <T> Table(
    modifier: Modifier = Modifier,
    horizontalScrollState: ScrollState = rememberScrollState(),
    columnCount: Int,
    data: List<T>,
    headerCellContent: @Composable BoxScope.(columnIndex: Int) -> Unit,
    cellContent: @Composable BoxScope.(columnIndex: Int, item: T) -> Unit
) {
    val columnWidths = remember { mutableStateMapOf<Int, Int>() }

    var columnHeight by remember { mutableStateOf(0) }

    Column(modifier = modifier.then(Modifier.horizontalScroll(horizontalScrollState))) {
        repeat(data.size + 1) { rowIndex ->
            Row {
                (0 until columnCount).forEach { columnIndex ->
                    Box(
                        modifier = Modifier
                            .border(0.dp, MaterialTheme.colors.onSurface)
                            .padding(Dimensions.Space.Large)
                            .layout { measurable, constraints ->
                                val placeable = measurable.measure(constraints)

                                val existingWidth = columnWidths[columnIndex] ?: 0
                                val maxWidth = maxOf(existingWidth, placeable.width)
                                val maxHeight = maxOf(columnHeight, placeable.height)

                                if (maxWidth > existingWidth) {
                                    columnWidths[columnIndex] = maxWidth
                                }
                                if (maxHeight > columnHeight) {
                                    columnHeight = maxHeight
                                }

                                val height = if (rowIndex == 0) placeable.height else maxHeight

                                layout(width = maxWidth, height = height) {
                                    placeable.placeRelative(0, 0)
                                }
                            }
                    ) {
                        if (rowIndex == 0) {
                            headerCellContent(columnIndex)
                        } else {
                            cellContent(columnIndex, data[rowIndex - 1])
                        }
                    }
                }
            }
        }
    }
}
Ive tried with maxContent on the cellContent and also tried adding a Box before allocating the slots but without any succes
a

Arjun Achatz

11/03/2022, 5:07 PM
I'd suggest looking at the jetlagged app, they did some heavy custom drawing
Which considered spans
It was in the latest Android Dev Summit
m

Marco Pierucci

11/03/2022, 5:10 PM
I might eventually, but I'd like to try and understand whats wrong with this? does not looks too crazy
z

Zach Klippenstein (he/him) [MOD]

11/03/2022, 5:35 PM
I don’t know how you’d do this without using intrinsics, which I don’t see in your code at all. In order to find the biggest size you have to measure everything with some constraints, but after you’ve done that you can’t re-measure everything again with the constraints from the largest item. Intrinsics let you query all the children to find which one should be biggest, then base the constraints for all children on that.
m

Marco Pierucci

11/03/2022, 5:44 PM
Mm I would have though that updatign the state which holds the max dimensions would remeasure with the largest size ( all blue cells have the largest size afterall) But I guess I'll give it a try with intrinsics thanks!
z

Zach Klippenstein (he/him) [MOD]

11/03/2022, 5:45 PM
there are problems with trying to hack this by doing it across multiple frames – besides the fact that the UI will flicker, you could potentially get into an infinite remeasure loop
m

Marco Pierucci

11/03/2022, 5:47 PM
yikes! Right intrinsics it is, thanks again
Ok so I got something quite similar to what I need thanks to the use of intrinsic, so many thanks one more time
Copy code
@Composable
fun <T> Table(
    modifier: Modifier = Modifier,
    horizontalScrollState: ScrollState = rememberScrollState(),
    columnCount: Int,
    data: List<T>,
    headerCellContent: @Composable BoxScope.(columnIndex: Int) -> Unit,
    cellContent: @Composable BoxScope.(columnIndex: Int, item: T) -> Unit
) {
    Row(modifier = Modifier.horizontalScroll(horizontalScrollState)) {
        (0 until columnCount).forEach { columnIndex ->
            Column(modifier = Modifier.width(IntrinsicSize.Max)) {
                (0..data.size).forEach { index ->
                    Box(
                        modifier = Modifier
                            .fillMaxSize()
                            .border(0.dp, MaterialTheme.colors.onSurface)
                    ) {
                        if (index == 0) {
                            headerCellContent(columnIndex)
                        } else {
                            cellContent(columnIndex, data[index - 1])
                        }
                    }
                }
            }
        }
    }
}
Now all my cells have the same width which is great. I dont think there is a way of querying the max height of the cells to apply it to be bo, with this current impl right? I'd need some sort of custom layout
a

Arjun Achatz

11/03/2022, 10:21 PM
Good stuff 🔥🔥 thanks for posting your findings. I'm really throwing a shot in the dark but I think parent modifier data might let you pass things back up the tree as a stateful thing
m

Marco Pierucci

11/03/2022, 10:24 PM
Ah forgot about this one, lol lots to learn yet, will try that tomorrow and post if I got something. Cheers!
112 Views