グリッドを含むワイドなクラシック カード レイアウト

4 つ星の評価アイコン

TV アプリのデザイン ガイドラインでは、画像とその説明コンテンツ ブロックを並べて表示する ワイド クラシック カードというカード バリアントが定義されています。 TV 画面用の複雑な 2 次元レイアウトを作成する場合、従来の座標ベースの配置や深くネストされた Row コンテナと Column コンテナを使用すると、コードの読み取り、保守、リファクタリングが難しくなる可能性があります。 ワイド クラシック カードは、Jetpack Compose Grid API の理想的なユースケースです。セマンティックなユーザー定義の 2D レイアウト領域を使用して、並列要素をすっきりと構造化できます。

5 つのレイアウト領域(画像、タイトル、サブタイトル、説明、追加テキスト)を示すワイド クラシック カード コンポーネントの仕様。
図 1.ワイド クラシック カードの 5 つの名前付き領域: 画像領域とコンテンツ ブロック内の 4 つのテキスト領域。

ベスト プラクティス

Grid を使用してカード レイアウトを実装します。Grid コンテナを使用すると、物理レイアウトの寸法と子アイテムの配置が分離されるため、コードの可読性が向上します。正確なトラック インデックスに依存するのではなく、area 関数を使用してセマンティックな名前付き領域を定義し、gridItem 修飾子を使用して子コンポーザブルを配置します。 柔軟なトラック サイズ設定(GridTrackSize.Autominmax(0.dp, 1.fr) など)と、正確なトラック間隔(gap(row = 8.dp, column = 16.dp))を組み合わせて、複雑な並列カード コンポーネントを明確かつ堅牢に整理します。

材料

  • Grid: 構成ラムダに基づいて子コンポーザブルを行と列に配置する Jetpack Compose の 2D コンテナ レイアウト
  • gridItem: Grid レイアウト内の特定の名前に関連付けられた子 UI 要素の Modifier 拡張機能 areaId
  • area: セマンティックな areaId を特定の行と列のスパンまたは範囲にマッピングする GridConfigurationScope 内の構成 DSL 関数
  • Card: 関連する UI 要素とスタイル設定をグループ化するマテリアル デザイン コンテナ コンポーザブル
  • GridTrackSize.MinMax: 行または列の寸法の柔軟な境界(minmax)を定義するトラック サイズ設定プロパティ
  • GridTrackSize.Auto: コンテンツに合わせて行または列 トラックのサイズを自動的に調整するトラック サイズ設定プロパティ(column(GridTrackSize.Auto)
  • gap: グリッド トラック間の垂直方向(row)と水平方向(column)の間隔を指定する構成関数

手順

領域識別子を定義し、グリッド トラックと領域を構成してから、子コンポーザブルを配置します。

1. セマンティックな領域識別子を定義する

まず、ワイド クラシック カード レイアウト内の領域の型安全な表現を作成します。enum クラスを使用すると、文字列の入力ミスを防ぎ、トラックのマッピングと子アイテムの配置時にコンパイル時の安全性を確保できます。

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

2. 2D グリッド トラックと名前付き領域を構成する

Grid コンポーザブルの config ラムダ内で、カード構造を構成する列と行を定義します。画像列には 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 修飾子を使用して、セマンティックな意図のみに基づいて各子 UI 要素を配置します。子コンポーザブルは任意の順序で宣言できます。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 アプリのデザイン ガイドラインに準拠しながら、レイアウトの可読性と保守性を高めることができます。

参考情報