
官方页面一、概念Column/Row 适合一维布局拼出二维布局即便 Compose 不怕嵌套但可读性差。LazyGrid 用于延迟加载大量的同质化条目就好比 ScrollView可滑动的Column 和 RecyclerViewLazyColumn 的区别。Grid 可以在多列或多行布局中显示子元素这些子元素尺寸还可以根据容器大小进行调整。后期改起来灵活性不行子元素多了协调比例会烦死人只在页面正好是个简单的网格比例可以用用属于封装了个细分场景。网格线网格轨道网格由水平和垂直的线条组成边框也是网格线。是两条网格线之间的空间。 行轨道位于两条水平线之间列轨道位于两条垂直线之间。可以在创建网格时定义轨道大小。网格单元网格区域是行轨道和列轨道的交集。由多个网格单元格组成。 可以通过 span 使项跨越多个轨道来定义网格区域。网格间距是网格轨道之间的间距。二、添加依赖[versions] compose 1.11.0-beta02 [libraries] androidx-compose-foundation-layout { group androidx.compose.foundation, name foundation-layout, version.ref compose }三、配置网格容器 Grid(config)Grid()Composableinline fun Grid(noinline config: GridConfigurationScope.() - Unit, //配置容器modifier: Modifier Modifier,content: Composable GridScope.() - Unit, //子元素)Grid( config { repeat(2) { column(160.dp) } repeat(3) { row(90.dp) } } ) {}3.1 定义行和列有几个 column() 就有几列按顺序定义不同列的宽度row() 同理。两者调用顺序没有规定可以交替写但影响可读性且无意义。即便不定义也会按照可用空间和固有尺寸进行自动填充和换行。默认填充方向轨道方向是水平一行一行按照列的顺序填充单元格。若 content 中的子元素数量不够填充未被填充的单元格依旧占位。若 content 中的子元素数量超出会新增行由于这行没有被指定单元格高度为该行最高的由于所有列都是已经被指定过的子元素的宽度超出不了。column()添加一列并指定列宽。fun column(size: Dp)固定大小。不推荐这样单元格尺寸不会跟着容器尺寸变化。底层通过 GridTrackSize.Fixed 实现。fun column(percentage: Float)百分比Grid的总可用空间取值范围 0 ~ 1F。底层通过 GridTrackSize.Percentage 实现。fun column(weight: Fr)剩余权重固定大小和百分比之后剩下的空间按权重分配。底层通过 GridTrackSize.Flex 实现。使用示例column(Fr(1F))。fun column(size: GridTrackSize)固有尺寸。参数 GridTrackSize.MaxContent使用内容的最大固有尺寸例如宽度由不换行的文本块中文字的完整长度决定。参数 GridTrackSize.MinContent使用内容的最小固有尺寸例如宽度由文本块中最长的单个字词决定。参数 GridTrackSize.Auto使用可根据可用空间调整的灵活轨道尺寸。默认情况下它的行为与 MaxContent 类似但会缩小并封装其内容以适应父级容器。参数 GridTrackSize.minMax剩余权重 GridTrackSize.Flex 会使用固有尺寸来确定最小尺寸当网格容器没有剩余空间时它会缩到 0dp。为了避免内容无法显示minMax 除了指定剩余权重还额外可强制指定最小尺寸。row()添加一行并指定行高。fun row(size: Dp)fun row(percentage: Float)fun row(weight: Fr)fun row(size: GridTrackSize)Grid( config { //可以交替写但影响可读性且无意义 column(100.dp) //第1列宽100dp column(200.dp) //第2列宽200dp row(100.dp) //第1行高100dp column(300.dp) //第3列宽300dp row(50.dp) //第2行高50dp //一般网格大小相同用 repeat() 添加 repeat(3) { column(200.dp) } repeat(2) { row(100.dp) } } ) {}3.1.1 为剩余权重设置大小限制高度一共就360dp第三行占200dp只剩180dp给其余两个分虽然一二行都设置了权重 1F但是第一行强制指定了最小尺寸因此一二行不是平分各80dp而是第一行100dp第二行60dp。Grid( config { column(1f) row(GridTrackSize.MinMax(100.dp, 1.fr)) row(1.fr) row(200.dp) }, modifier Modifier.size(360.dp) ) { PastelRedCard(MinMax(100.dp, 1.fr)) PastelGreenCard(Flex(1.fr)) PastelBlueCard(Fixed(200.dp)) }3.1.2 将 LazyList 放入剩余权重网格设置剩余权重的轨道会查询子元素的固有尺寸但 Compose 禁止查询 SubcomposeLayoutLazyList 用其实现的固有大小会报错 IllegalStateException。使用 minMax 明确指定最小尺寸解决。Grid( config { column(1f) // 第一行的高度由 Text 可组合项的高度决定。 row(GridTrackSize.Auto) // 第二行占据剩余空间从而使 LazyColumn 能够滚动。 row(GridTrackSize.MinMax(0.dp, 1.fr)) gap(8.dp) }, modifier Modifier.size(width 170.dp, height 240.dp) ) { Text(LazyColumn in a Grid) // LazyColumn 被放置在第二行并填满剩余空间。 LazyColumn(verticalArrangement Arrangement.spacedBy(4.dp)) { items(100) { number - PastelGreenCard(Card $number) } } }3.2 填充方向轨道方向flowvar flow: GridFlowflow GridFlow.Row 默认一行一行按列填充单元格flow GridFlow.Column一列一列按行填充单元格Grid( config { flow GridFlow.Column } }3.3 间距columnGap()fun columnGap(gap: Dp)列间距。rowGap()fun rowGap(gap: Dp)行间距。gap()fun gap(all: Dp)同时设置。Grid( config { gap(8.dp) } }四、配置网格单元 Modifier.gridItem()索引从1开始如果不同时指定 column 和 row 则会将子元素放置在该轨道方向中的下一个可用单元格中。使用正数从轨道起始位置正向填充到索引位置使用负数从轨道结束位置反向填充到索引位置。自动填充的子元素会避开手动指定位置的子元素接着填充。gridItem()fun Modifier.gridItem(AndroidXIntRange(from -MaxGridIndex.toLong(), to MaxGridIndex.toLong())row: Int GridIndexUnspecified, //指定行索引AndroidXIntRange(from -MaxGridIndex.toLong(), to MaxGridIndex.toLong())column: Int GridIndexUnspecified, //指定列索引AndroidXIntRange(from 1) rowSpan: Int 1, //填充几行AndroidXIntRange(from 1) columnSpan: Int 1, //填充几列alignment: Alignment Alignment.TopStart, //元素在网格区域中的对齐方式): Modifier4.1 指定填充位置Grid( config { repeat(2) { column(160.dp) } repeat(3) { row(90.dp) } gap(8.dp) } ) { Card1() Card2(modifier Modifier.gridItem(row 2, column 2)) Card3(modifier Modifier.gridItem(row -1, column -2)) }4.2 跨行跨列Grid( config { repeat(3) { column(160.dp) } repeat(3) { row(90.dp) } rowGap(8.dp) columnGap(8.dp) } ) { Card1(modifier Modifier.gridItem(rowSpan 2, columnSpan 2)) Card2() Card3() Card4(modifier Modifier.gridItem(columnSpan 3)) }4.3 网格区域中的对齐方式Grid( config { repeat(3) { column(160.dp) } repeat(3) { row(90.dp) } rowGap(8.dp) columnGap(8.dp) }, ) { Text( text #1, modifier Modifier .gridItem( rowSpan 2, columnSpan 2, alignment Alignment.Center ), ) Card2() Card3() Card4(modifier Modifier.gridItem(columnSpan 3)) }4.4 自动填充与手动指定位置共有6个子元素指定了2和4剩余的 1 3 5 6 按顺序填充未指定的单元格。Grid( config { repeat(2) { column(160.dp) } repeat(3) { row(90.dp) } rowGap(16.dp) columnGap(8.dp) } ) { Card1() Card2(modifier Modifier.gridItem(row 2, column 2)) Card3() Card4(modifier Modifier.gridItem(row 3, column 1)) Card5() Card6() }4.5 命名网格区域来填充在网格中放置界面元素时可以使用这些名称而不是坐标索引可读性大大提升。在 config lambda 中使用 GridConfigurationScope 中的 area 函数在网格中注册命名区域。然后您可以使用 gridItem 修饰符和相应的区域标识符将子可组合项分配给这些区域。area 函数将语义标识符例如 enum 类值或字符串键映射到一组物理网格坐标。网格线和索引从 1 开始即第一行为 1第一列为 1。area()fun area(areaId: Any, // 名称row: Int GridIndexUnspecified, // 横坐标column: Int GridIndexUnspecified, // 纵坐标rowSpan: Int 1, // 行跨度columnSpan: Int 1, // 列跨度)enum class GridAreaNames { Area1, Area2, Area3, Area4 }Grid( config { // 定义一个占据所有可用宽度的单列。 repeat(2) { column(0.5f) } // 定义四行每行占总高度的 25%。 repeat(4) { row(0.25f) } // 通过将 areaId 与特定的行和列索引相关联来定义命名网格区域。 // 行和列的索引从 1 开始。 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, // 将此可组合项放置到配置块中定义的、具有匹配 ID 的网格区域中。 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) ) }