设置容器属性

您可以定义网格容器配置,以创建可根据不同屏幕尺寸和内容类型进行调整的灵活布局。 本页面介绍了如何执行以下操作:

定义网格

网格由列和行组成。 Grid 可组合项具有 config 参数,该参数接受一个 lambda 来定义 GridConfigurationScope 中的列和行。 以下示例定义了一个包含三行两列的网格,每行和每列都具有在 Dp 中指定的固定大小:

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
    }
) {
}

将内容放置在网格中

Grid 会获取 content lambda 中的界面元素,并将它们放置到网格单元中。无论您是否明确定义了行和列,网格都会布局列表项。 默认情况下,Grid 会尝试将界面元素放置在行中的可用网格单元格中;如果无法放置,则会将其放置在下一行中的可用网格单元格中。如果没有空单元格,Grid 会创建新行。

在以下示例中,网格有 6 个网格单元,每个单元中放置一张卡片(图 1)。 每个网格单元的大小为 160dp x 90dp,因此整个网格的大小为 320dp x 270dp

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
    }
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

六张卡片放置在具有三行两列的网格中。
图 1。 六张卡片放置在具有三行两列的网格中。

如需将此默认行为更改为按列填充,请将 flow 属性设置为 GridFlow.Column

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
        gap(8.dp)
        flow = GridFlow.Column // Grid tries to place items to fill the column
    },
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

流函数会更改放置商品的方向。
图 2GridFlow.Row(左侧)和 GridFlow.Column(右侧)。

管理轨道尺寸

行和列统称为网格轨道。 您可以使用以下任一方法指定网格轨道的大小:

  • 固定 (Dp):分配特定大小(例如 column(180.dp))。
  • 百分比 (Float):分配总可用空间中从 0.0f1.0f 的百分比(例如,row(0.5f) 表示 50%)。
  • 灵活 (Fr):在计算完固定大小和百分比大小的轨道后,按比例分配剩余空间。例如,如果两行的值分别设置为 1.fr3.fr,则后者会获得剩余高度的 75%。
  • 固有:根据轨道内的内容调整轨道大小。如需了解详情,请参阅内在确定网格轨道大小

以下示例使用不同的轨道尺寸调整选项来定义行高:

Grid(
    config = {
        column(1f)

        row(100.dp)
        row(0.2f)
        row(1.fr)
        row(GridTrackSize.Auto)
    },
    modifier = Modifier.height(480.dp)
) {
    PastelRedCard("Fixed(100.dp)")
        PastelGreenCard("Percentage(0.2f)")
    PastelBlueCard("Flex(1.fr)")
        PastelYellowCard("Auto")

}

使用四种主要轨道尺寸调整选项定义的行高。
图 3。 使用 Grid 中的四种主要轨道尺寸调整选项定义的行高。

为灵活网格轨道设置最小尺寸

当网格容器没有剩余空间时,标准灵活轨道可以缩小到 0.dp。为防止这种情况并确保内容不会被压缩,请使用 GridTrackSize.MinMax 来强制设置明确的最小尺寸,同时保持轨道灵活。

以下示例为第一行分配了至少 100.dp

Grid(
    config = {
        column(1f)
        // The first row has a minimum height of 100.dp and can expand to 
        // the half of the remaining space.
        row(GridTrackSize.MinMax(100.dp, 1.fr))
        // The second row takes the half of the remaining space.
        row(1.fr)
        // The third row has a fixed height of 200.dp.
        row(200.dp)
    },
    modifier = Modifier.size(360.dp) // Total grid height is 360.dp
) {
    PastelRedCard("MinMax(100.dp, 1.fr)")
        PastelGreenCard("Flex(1.fr)")
    PastelBlueCard("Fixed(200.dp)")
}

使用四种主要轨道尺寸调整选项定义的行高。
图 4。 第一行的高度至少为 100.dp

设置放置延迟列表的最小网格轨道大小

标准灵活轨道会自动查询其子项的固有尺寸,以确定基本尺寸。不过,Jetpack Compose 禁止查询支持组件(例如 LazyColumnLazyRow)的 SubcomposeLayout 的固有大小。

将延迟列表放置在标准灵活轨道中会导致 IllegalStateException 崩溃。 如需将延迟列表安全地放置在灵活的网格轨道内,请使用具有明确最小尺寸(例如 0.dp)的 MinMax 来绕过固有测量传递。

Grid(
    config = {
        column(1f)
        // The first row's height is determined by the height of the Text composable.
        row(GridTrackSize.Auto)
        // The second row occupies the remaining space, allowing the LazyColumn to scroll.
        row(GridTrackSize.MinMax(0.dp, 1.fr))

        gap(8.dp)
    },
    modifier = Modifier.size(width = 170.dp, height = 240.dp)
) {
    Text("LazyColumn in a Grid")
    // The LazyColumn is placed in the second row, filling the remaining space.
    LazyColumn(verticalArrangement = Arrangement.spacedBy(4.dp)) {
        items(100) { number ->
            PastelGreenCard("Card $number")
        }
    }
}

使用四种主要轨道尺寸调整选项定义的行高。
图 5LazyColumn 在网格单元格中。

内在确定网格轨道大小

如果您希望布局适应内容,而不是强制将其放入固定容器中,则可以为 Grid 使用固有尺寸调整。网格轨道大小由以下值确定:

  • GridTrackSize.MaxContent:使用内容的最大固有大小(例如,宽度由不换行的文本块中文字的完整长度决定)。
  • GridTrackSize.MinContent:使用内容的最小固有尺寸(例如,宽度由文本块中最长的单个字词决定)。
  • GridTrackSize.Auto:使用可根据可用空间调整的灵活轨道尺寸。默认情况下,它的行为与 MaxContent 类似,但会缩小并包裹其内容以适应父级容器。

以下示例将两段文字并排放置。 第一个文本的列大小由显示文本所需的最小宽度决定,而第二个列宽度取决于文本所需的最大宽度。

Grid(
    config = {
        column(GridTrackSize.MinContent)
        column(GridTrackSize.MaxContent)
        row(1.0f)
    },
    modifier = Modifier.width(480.dp)
) {
    Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras imperdiet.")
    Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras imperdiet.")
}

列中指定的固有大小。
图 5。 列中指定的固有大小。

设置行和列之间的间距

网格轨道设置好大小后,您可以修改网格间距,以调整轨道之间的间距。 您可以使用 columnGap 函数指定列间距,并使用 rowGap 指定行间距。在以下示例中,每行之间有 16dp 的间距,每列之间有 8dp 的间距(图 5)。

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
        rowGap(16.dp)
        columnGap(8.dp)
    }
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

行与列之间的间距。
图 6。 行和列之间的间距。

您还可以使用便捷函数 gap 来定义相同列大小和行大小的间距,以及使用单个函数分别定义列大小和间距大小。以下代码会向网格添加 8dp 间距:

Grid(
    config = {
        repeat(2) {
            column(160.dp)
        }
        repeat(3) {
            row(90.dp)
        }
        gap(8.dp) // Equivalent to columnGap(8.dp) and rowGap(8.dp)
    }
) {
    Card1()
    Card2()
    Card3()
    Card4()
    Card5()
    Card6()
}

使用命名区域定义网格区域

通过命名区域,您可以为网格单元组(称为网格区域)附加名称。 在网格中放置界面元素时,您可以使用这些名称而不是坐标索引

使用命名区域对代码可读性有两大好处:

  • 在定义网格布局时,预期内容的用途和位置一目了然。
  • 添加内容时,该内容的用途明确。

为了清晰地组织复杂的布局,您可以通过定义语义网格区域将物理网格结构与子级放置位置分离。

config lambda 中,使用 GridConfigurationScope 中的 area 函数在网格中注册命名区域。 然后,您可以使用带有相应区域标识符的 gridItem 修饰符将子可组合项分配给这些区域。area 函数将语义标识符(例如 enum 类值或字符串键)映射到一组物理网格坐标。网格线和索引从 1 开始(即第一行为 1,第一列为 1)。

例如,您定义了一个包含 4 个区域 ID 的网格:

/**
 * An enum representing the IDs for named areas within the grid.
 */
enum class GridAreaNames {
    Area1,
    Area2,
    Area3,
    Area4
}

使用 areaId 参数提供区域的名称,以及区域的单元格坐标和跨度。gridItem 修饰符使用 areaId 作为键,将每个子项分配到其指定的网格区域,如以下示例所示:

Grid(
    config = {
        // Define a single column that takes all available width.
        repeat(2) { column(0.5f) }

        // Define four rows, each taking 25% of the total height.
        repeat(4) { row(0.25f) }

        // Define named grid areas by associating an areaId with specific row and column indices.
        // Row and column indices are 1-based.
        area(areaId = GridAreaNames.Area1, row = 1, column = 1, columnSpan = 2)
        area(areaId = GridAreaNames.Area2, row = 2, column = 1, rowSpan = 3)
        area(areaId = GridAreaNames.Area3, rows = 2..3, columns = 2..2)
        area(areaId = GridAreaNames.Area4, row = 4, column = 2)

        gap(4.dp)
    },
    modifier = Modifier.size(360.dp)
) {
    PastelRedCard(
        "Area 1",
        // Use Modifier.gridItem(areaId) to place this composable into the
        // grid area defined with the matching ID in the config block.
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area1)
    )
    PastelGreenCard(
        "Area 2",
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area2)
    )
    PastelBlueCard(
        "Area 3",
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area3)
    )
    PastelYellowCard(
        "Area 4",
        modifier = Modifier.gridItem(areaId = GridAreaNames.Area4)
    )
}

通过使用命名区域,您可以在 config lambda 中重新组织或调整实体布局网格(例如,更改行、列或轨道大小),而无需修改子可组合项的顺序或参数。