如何使用Jetpack compose实现交错的网格布局?

时间:2020-10-07 04:54:24

标签: android android-recyclerview android-jetpack-compose

据我所知,我们只能使用Rows中的ColumnsJetpack Compose来显示列表。如何实现交错的网格布局,如下图所示?使用Recyclerview和交错的网格布局管理器对其进行常规实现非常容易。但是如何在Jetpack Compose中做同样的事情?

Staggered grid layout

2 个答案:

答案 0 :(得分:4)

Google的撰写示例Owl之一显示了如何进行交错的网格布局。这是用于编写此代码的代码段:

@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
}

然后您可以传入可组合的项目:

StaggeredVerticalGrid(
    maxColumnWidth = 220.dp,
    modifier = Modifier.padding(4.dp)
) {
    // Use your item composable here
}

链接到示例中的代码段:https://github.com/android/compose-samples/blob/1630f6b35ac9e25fb3cd3a64208d7c9afaaaedc5/Owl/app/src/main/java/com/example/owl/ui/courses/FeaturedCourses.kt#L161

答案 1 :(得分:1)

您的布局是可滚动的布局,其中包含多张卡片(2或4)的行

包含2个项目的行:

@Composable
fun GridRow2Elements(row: RowData) {
  Row(
    modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight(),
    horizontalArrangement = Arrangement.SpaceEvenly
  ) {
    GridCard(row.datas[0], small = true, endPadding = 0.dp)
    GridCard(row.datas[1], small = true, startPadding = 0.dp)
  } 
}

包含4个项目的行:

@Composable
fun GridRow4Elements(row: RowData) {
 Row(
    modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight(),
    horizontalArrangement = Arrangement.SpaceEvenly
 ) {
    Column {
        GridCard(row.datas[0], small = true, endPadding = 0.dp)
        GridCard(row.datas[1], small = false, endPadding = 0.dp)
    }
    Column {
        GridCard(row.datas[2], small = false, startPadding = 0.dp)
        GridCard(row.datas[3], small = true, startPadding = 0.dp)
    }
 }
}

最终的网格布局:

@Composable
fun Grid(rows: List<RowData>) {
ScrollableColumn(modifier = Modifier.fillMaxWidth()) {
    rows.mapIndexed { index, rowData ->
        if (rowData.datas.size == 2) {
            GridRow2Elements(rowData)
        } else if (rowData.datas.size == 4) {
            GridRow4Elements(rowData)
        }
    }
} 

enter image description here

然后,您可以使用所需的卡布局进行自定义。我为大型和大型卡设置了静态值(高度为120,高度为270,宽度为170)

@Composable
fun GridCard(
 item: Item,
 small: Boolean,
 startPadding: Dp = 8.dp,
 endPadding: Dp = 8.dp,
) {
 Card(
    modifier = Modifier.preferredWidth(170.dp)
        .preferredHeight(if (small) 120.dp else 270.dp)
        .padding(start = startPadding, end = endPadding, top = 8.dp, bottom = 8.dp)
) {
 ...
}

 

我将数据转换为:

data class RowData(val datas: List<Item>)
data class Item(val text: String, val imgRes: Int)

您只需用

调用它
 val listOf2Elements = RowData(
    listOf(
        Item("Zesty Chicken", xx),
        Item("Spring Rolls", xx),
    )
)

val listOf4Elements = RowData(
    listOf(
        Item("Apple Pie", xx),
        Item("Hot Dogs", xx),
        Item("Burger", xx),
        Item("Pizza", xx),
    )
)

Grid(listOf(listOf2Elements, listOf4Elements))

确定您需要仔细管理数据转换,因为您可以使用带有data [index]的ArrayIndexOutOfBoundsException