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