Diseño de tarjeta clásico ancho con cuadrícula

Ícono de calificación de cuatro estrellas

Los lineamientos de diseño de la app para TV definen una variante de tarjeta llamada tarjeta clásica ancha, que muestra una imagen y su bloque de contenido descriptivo uno al lado del otro. Cuando compilas diseños bidimensionales complejos para pantallas de TV, el uso del posicionamiento tradicional basado en coordenadas o los contenedores Row y Column anidados en profundidad pueden dificultar la lectura, el mantenimiento y la refactorización del código. La tarjeta clásica ancha es un caso de uso ideal para la API de Grid de Jetpack Compose, que te ayuda a estructurar elementos uno al lado del otro de forma clara con áreas de diseño semánticas y bidimensionales definidas por el usuario.

Especificación del componente de tarjeta clásica ancha que muestra cinco áreas de diseño con nombre:
      Imagen, Título, Subtítulo, Descripción y Texto adicional.
Figura 1. Tarjeta clásica ancha con cinco áreas con nombre: área de imagen y cuatro áreas de texto en el bloque de contenido.

Prácticas recomendadas

Implementa el diseño de la tarjeta con Grid. El uso del contenedor Grid mejora la legibilidad del código, ya que desacopla las dimensiones del diseño físico de la ubicación del elemento secundario. En lugar de depender de los índices de seguimiento exactos, define áreas semánticas con nombre con la función area y coloca elementos componibles secundarios con el modificador gridItem. Combina el tamaño flexible de seguimiento, como GridTrackSize.Auto y minmax(0.dp, 1.fr), con espacios de seguimiento exactos (gap(row = 8.dp, column = 16.dp)) para organizar componentes de tarjetas complejos uno al lado del otro de forma clara y sólida.

Ingredientes

  • Grid: Un diseño de contenedor 2D en Jetpack Compose que organiza elementos componibles secundarios en filas y columnas según una expresión lambda de configuración
  • gridItem: Una extensión Modifier que asocia un elemento de la IU secundario con un areaId con nombre específico dentro de un diseño Grid
  • area: Una función DSL de configuración dentro de GridConfigurationScope que asigna un areaId semántico a rangos o intervalos de filas y columnas específicos
  • Card: Un elemento componible de contenedor de Material Design que agrupa elementos de la IU y estilos relacionados
  • GridTrackSize.MinMax: Una propiedad de tamaño de seguimiento que define límites flexibles (min y max) para las dimensiones de filas o columnas
  • GridTrackSize.Auto: Una propiedad de tamaño de seguimiento que ajusta automáticamente el tamaño del seguimiento de filas o columnas para que se ajuste a su contenido (column(GridTrackSize.Auto))
  • gap: Una función de configuración que especifica el espaciado vertical (row) y horizontal (column) entre los seguimientos de la cuadrícula

Pasos

Define los identificadores de área, configura los seguimientos y las áreas de la cuadrícula y, luego, coloca los elementos componibles secundarios.

1. Define identificadores de área semánticos

Primero, crea una representación con seguridad de tipos de las regiones dentro del diseño de la tarjeta clásica ancha. Una clase enum evita errores tipográficos de cadenas y proporciona seguridad en tiempo de compilación cuando se asignan seguimientos y se colocan elementos secundarios.

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

2. Configura los seguimientos y las áreas con nombre de la cuadrícula 2D

Dentro de la expresión lambda config del elemento componible Grid, define las columnas y las filas que componen la estructura de la tarjeta. Usa GridTrackSize.Auto para la columna de la imagen de modo que ajuste el ancho de la miniatura de forma clara. Usa minmax(0.dp, 1.fr) para la columna de contenido de modo que se expanda de forma flexible en el ancho restante de la tarjeta. Especifica el espaciado de seguimiento con gap(row = 8.dp, column = 16.dp). Asigna tus identificadores CardArea a sus coordenadas de seguimiento físicas con la función area.

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. Coloca elementos componibles secundarios con el modificador gridItem

Con los seguimientos y las áreas configurados, coloca cada elemento de la IU secundario únicamente por intención semántica con el modificador gridItem. Puedes declarar elementos componibles secundarios en cualquier orden. El contenedor Grid posiciona cada elemento directamente en su región 2D asignada de forma automática.

@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),
            )
        }
    }
}

Resultados

Si organizas tu tarjeta clásica ancha con Grid y áreas con nombre, lograrás una separación clara entre los seguimientos de diseño físico, las coordenadas de área y la ubicación de elementos componibles secundarios. Esta estructura hace que tu diseño sea legible y fácil de mantener, a la vez que sigue los lineamientos de diseño de la app para TV para los componentes de miniatura y descripción uno al lado del otro.

Recursos adicionales