1
votes

As of Jetpack Compose, a Recycler type of view is called Lazy.

For now, there are only Lazy Row and Lazy Column.

I have a Staggered Grid custom Composable, that is scrollable. But is there a way I can make it Lazy? Or is such an API not stable yet?

2

2 Answers

3
votes

You can do the same effect in many ways, one of them is:

@Composable
fun StaggeredVerticalGrid(
    modifier: Modifier = Modifier,
    maxColumnWidth: Dp,
    children: @Composable () -> Unit
) {
    Layout(
        children = children,
        modifier = modifier
    ) { measurables, constraints ->
        check(constraints.hasBoundedWidth) {
            "Unbounded width not supported"
        }
        val columns = ceil(constraints.maxWidth / maxColumnWidth.toPx()).toInt()
        val columnWidth = constraints.maxWidth / columns
        val itemConstraints = constraints.copy(maxWidth = columnWidth)
        val colHeights = IntArray(columns) { 0 } // track each column's height
        val placeables = measurables.map { measurable ->
            val column = shortestColumn(colHeights)
            val placeable = measurable.measure(itemConstraints)
            colHeights[column] += placeable.height
            placeable
        }

        val height = colHeights.maxOrNull()?.coerceIn(constraints.minHeight, constraints.maxHeight)
            ?: constraints.minHeight
        layout(
            width = constraints.maxWidth,
            height = height
        ) {
            val colY = IntArray(columns) { 0 }
            placeables.forEach { placeable ->
                val column = shortestColumn(colY)
                placeable.place(
                    x = columnWidth * column,
                    y = colY[column]
                )
                colY[column] += placeable.height
            }
        }
    }
}

private fun shortestColumn(colHeights: IntArray): Int {
    var minHeight = Int.MAX_VALUE
    var column = 0
    colHeights.forEachIndexed { index, height ->
        if (height < minHeight) {
            minHeight = height
            column = index
        }
    }
    return column
}

extracted from Owl sample app, Don't forget to wrap/call this Composable inside ScrollableColumn.

EDIT: with the release of compose 1.0.0-alpha09, now you have official LazyVerticalGrid, check release notes

0
votes

If you are implementing a custom staggered grid, I would say you implement a staggered row instead. Then create another Composable that receives the data and feeds it in the staggered rows while internally using a lazy column for each row. I am not asking you to implement something new. Just tweak the existing implementation a bit and it will be a great performance boost.

Also, if your data is not big enough, it is okay to not use lazy containers.