采用网格布局的宽版经典卡片

4 星级图标

TV 应用设计指南定义了一种名为宽经典卡片的卡片变体,该变体并排显示图片及其说明性内容块。在为电视屏幕构建复杂的二维布局时,使用传统的基于坐标的定位或深度嵌套的 Row 和 Column 容器可能会使代码难以阅读、维护和重构。 宽版经典卡片是 Jetpack Compose Grid API 的理想应用场景,可帮助您使用语义化、用户定义的 2D 布局区域,清晰地构建并排元素。

宽版经典卡片组件的规格,其中显示了五个命名的布局区域:图片、标题、副标题、说明和额外文本。
图 1. 宽幅经典卡片 5 的五个命名区域:图片区域和内容块中的四个文本区域。

最佳做法

使用 Grid 实现卡片布局。 使用 Grid 容器可将物理布局尺寸与子项放置位置分离开来,从而提高代码可读性。不要依赖确切的轨道索引, 而是使用 area 函数定义语义命名区域, 并使用 gridItem 修饰符放置子级可组合项。 将灵活的轨道大小调整(例如 GridTrackSize.Auto 和 minmax(0.dp, 1.fr))与精确的轨道间隙 (gap(row = 8.dp, column = 16.dp)) 相结合,以清晰、稳健的方式整理复杂的并排卡片组件。

所需要素

  • Grid:Jetpack Compose 中的一种 2D 容器布局,可根据配置 lambda 将子可组合项排列成行和列
  • gridItem:一个 Modifier 扩展程序,用于将子界面元素与 Grid 布局中特定命名的 areaId 相关联
  • area:GridConfigurationScope 中的一个配置 DSL 函数,用于将语义 areaId 映射到特定的行和列跨度或范围
  • Card:一个 Material Design 容器可组合项,用于将相关的界面元素和样式分组
  • GridTrackSize.MinMax:一种轨道大小调整属性,用于为行或列维度定义灵活的边界(min 和 max)
  • GridTrackSize.Auto:一种轨道大小调整属性,可自动调整行或列轨道的大小,以适应其内容 (column(GridTrackSize.Auto))
  • gap:一个配置函数,用于指定网格轨道之间的垂直 (row) 和水平 (column) 间距

步骤

定义区域标识符,配置网格轨道和区域,然后放置子级可组合项。

1. 定义语义区域标识符

首先,为宽经典卡片布局中的区域创建类型安全的表示。enum 类可避免字符串拼写错误,并在映射轨道和放置子项时提供编译时安全性。

enum class CardArea {
    Image,
    Title,
    Subtitle,
    Description,
    ExtraText,
}

2. 配置 2D 网格轨道和命名区域

在 Grid 可组合项的 config lambda 中,定义构成卡片结构的列和行。对图片列使用 GridTrackSize.Auto,使其能够完美地封装缩略图宽度。对内容列使用 minmax(0.dp, 1.fr),使其在剩余的卡片宽度内灵活展开。使用 gap(row = 8.dp, column = 16.dp) 指定轨道间距。使用 area 函数将 CardArea 标识符映射到其物理轨道坐标。

Grid(
    config = {
        // Define columns: left column for image, right for content
        column(GridTrackSize.Auto)
        column(minmax(0.dp, 1.fr))

        // Define row tracks for the vertical content stack
        row(GridTrackSize.Auto)
        row(GridTrackSize.Auto)
        row(GridTrackSize.Auto)
        row(GridTrackSize.Auto)

        // Map semantic identifiers to grid coordinates and spans
        area(CardArea.Image, row = 1, column = 1, rowSpan = 4)
        area(CardArea.Title, row = 1, column = 2)
        area(CardArea.Subtitle, row = 2, column = 2)
        area(CardArea.Description, row = 3, column = 2)
        area(CardArea.ExtraText, row = 4, column = 2)

        gap(row = 8.dp, column = 16.dp)
    }
) {
    // Child elements placed in Step 3
}

3. 使用 gridItem 修饰符放置子级可组合项

配置好轨道和区域后,使用 gridItem 修饰符,纯粹根据语义意图放置每个子界面元素。您可以按任意顺序声明子级可组合项。Grid 容器会自动将每个元素直接放置到其分配的 2D 区域中。

@Composable
fun WideClassicCard(
    imageContent: @Composable () -> Unit,
    title: String,
    subtitle: String,
    description: String,
    extraText: String,
    modifier: Modifier = Modifier,
) {
    Card(modifier = modifier) {
        Grid(
            config = {
                // Define columns: left column for image, right for content
                column(GridTrackSize.Auto)
                column(minmax(0.dp, 1.fr))

                // Define row tracks for the vertical content stack
                row(GridTrackSize.Auto)
                row(GridTrackSize.Auto)
                row(GridTrackSize.Auto)
                row(GridTrackSize.Auto)

                // Map semantic identifiers to grid coordinates and spans
                area(CardArea.Image, row = 1, column = 1, rowSpan = 4)
                area(CardArea.Title, row = 1, column = 2)
                area(CardArea.Subtitle, row = 2, column = 2)
                area(CardArea.Description, row = 3, column = 2)
                area(CardArea.ExtraText, row = 4, column = 2)

                gap(row = 8.dp, column = 16.dp)
            },
        ) {
            Box(modifier = Modifier.gridItem(CardArea.Image)) {
                imageContent()
            }
            Text(
                text = title,
                style = MaterialTheme.typography.titleLarge,
                modifier = Modifier
                    .gridItem(CardArea.Title)
                    .padding(top = 16.dp, end = 16.dp),
            )

            Text(
                text = subtitle,
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier
                    .gridItem(CardArea.Subtitle)
                    .padding(end = 16.dp),
            )

            Text(
                text = description,
                style = MaterialTheme.typography.bodyMedium,
                modifier = Modifier
                    .gridItem(CardArea.Description)
                    .padding(end = 16.dp),
            )

            Text(
                text = extraText,
                style = MaterialTheme.typography.labelSmall,
                modifier = Modifier
                    .gridItem(CardArea.ExtraText)
                    .padding(bottom = 16.dp, end = 16.dp),
            )
        }
    }
}

结果

通过使用 Grid 和命名区域来整理宽版经典卡片,您可以实现物理布局轨道、区域坐标和子可组合项放置之间的清晰分离。 这种结构可让您的布局易于阅读和维护,同时遵循 TV 应用设计准则,并排显示缩略图和说明组件。

其他资源