TV 应用设计指南定义了一种名为“宽经典卡片”的卡片变体,该变体并排显示图片及其描述性内容块。在为 TV 屏幕构建复杂的二维布局时,使用基于坐标的传统定位或深度嵌套的 Row 和 Column 容器可能会导致代码难以阅读、维护和重构。
宽经典卡片是 Jetpack Compose Grid API 的理想用例,可帮助您使用语义化的用户定义二维布局区域来清晰地构建并排元素。
最佳做法
使用 Grid 实现卡片布局。
使用 Grid 容器可将物理布局尺寸与子项放置位置分离,从而提高代码的可读性。
您可以使用 area 函数定义语义化的命名区域,并使用 gridItem 修饰符放置子可组合项,而不是依赖于确切的轨道索引。
将灵活的轨道大小调整(例如 GridTrackSize.Auto
和 minmax(0.dp, 1.fr),
)与确切的轨道间距 (gap(row = 8.dp, column = 16.dp))
相结合,可以清晰且稳健地组织复杂的并排卡片组件。
所需要素
Grid:Jetpack Compose 中的二维容器布局,可根据配置 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. 配置二维网格轨道和命名区域
在 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 应用设计指南中有关并排缩略图和说明组件的规定。