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

四星评级图标

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

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

最佳做法

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

所需要素

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

步骤

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

1. 定义语义化区域标识符

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

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

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

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 容器会自动将每个元素直接放置到其分配的二维区域中。

@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 应用设计指南中有关并排缩略图和说明组件的规定。

其他资源