Cómo crear un navegador de catálogos

Una app de música que se ejecute en una TV debe permitir a los usuarios explorar sus ofertas de contenido, hacer una selección y comenzar a reproducir contenido. La experiencia de navegación por el contenido de este tipo de apps debe ser intuitiva y simple, visualmente agradable y atractiva.

En esta sección, se describe cómo usar las funciones que proporciona Compose para TV para implementar una interfaz de usuario que permita explorar música o videos del catálogo multimedia de tu app.

Figura 1: Pantalla de catálogo habitual. Los usuarios pueden explorar los datos del catálogo de videos.

Un navegador de catálogo de contenido multimedia suele tener varias secciones, y cada una tiene una lista de contenido multimedia. Los ejemplos de secciones en un catálogo multimedia incluyen: listas de reproducción,

Cómo crear una función de componibilidad para catálogo

Todo lo que aparece en una pantalla se implementa como una función de componibilidad en Compose para TV. Comenzarás por definir una función de componibilidad para el navegador de catálogo de contenido multimedia como el siguiente fragmento:

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
// ToDo: add implementation
}

CatalogBrowser es la función de componibilidad que implementa tu navegador de catálogo de contenido multimedia. La función toma los siguientes argumentos:

  • Es la lista de contenido destacado.
  • Lista de secciones.
  • Es un objeto Modifier.
  • Una función de devolución de llamada, que activa una transición de pantalla

Cómo establecer elementos de IU

Compose para TV ofrece listas diferidas, un componente para mostrar una gran cantidad de elementos (o una lista de longitud desconocida). Llamarás a TvLazyColumn para colocar secciones verticalmente. TvLazyColumn proporciona un bloque TvLazyListScope.() -> Unit, que ofrece un DSL para definir el contenido del elemento. En el siguiente ejemplo, cada sección se coloca en una lista vertical con un espacio de 16 dp entre las secciones.

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
  TvLazyColumn(
    modifier = modifier.fillMaxSize(),
    verticalArrangement = Arrangement.spacedBy(16.dp)
  ) {
    items(sectionList) { section ->
      Section(section, onItemSelected = onItemSelected)
    }
  }
}

En el ejemplo, la función de componibilidad Section define cómo mostrar secciones. En la siguiente función, TvLazyRow demuestra cómo esta versión horizontal de TvLazyColumn se usa de manera similar para definir una lista horizontal con un bloque TvLazyListScope.() -> Unit llamando al DSL proporcionado.

@Composable
fun Section(
  section: Section,
  modifier: Modifier = Modifier,
  onItemSelected: (Movie) -> Unit = {},
) {
  Text(
    text = section.title,
    style = MaterialTheme.typography.headlineSmall,
  )
  TvLazyRow(
     modifier = modifier,
     horizontalArrangement = Arrangement.spacedBy(8.dp)
  ) {
    items(section.movieList){ movie ->
    MovieCard(
         movie = movie,
         onClick = { onItemSelected(movie) }
       )
    }
  }
}

En el elemento componible Section, se usa el componente Text. El texto y otros componentes definidos en Material Design se ofrecen en la biblioteca de tv-material . Puedes cambiar el estilo de los textos como se define en Material Design consultando el objeto MaterialTheme. La biblioteca de tv-material también proporciona este objeto. MovieCard define la manera en que se renderizan los datos de cada película en el catálogo definido como el siguiente fragmento. Card también forma parte de la biblioteca de tv-material.

@Composable
fun MovieCard(
   movie: Movie,
   modifier: Modifier = Modifier,
   onClick: () -> Unit = {}
) {
   Card(modifier = modifier, onClick = onClick){
    AsyncImage(
       model = movie.thumbnailUrl,
       contentDescription = movie.title,
     )
   }
}

En el ejemplo descrito anteriormente, todas las películas se muestran de la misma manera. Tienen la misma área, no hay diferencia visual entre ellos. Puedes destacar algunos con Carousel.

El carrusel muestra la información en un conjunto de elementos que pueden deslizarse, atenuarse o moverse a la vista. Úsalo para destacar contenido destacado, como películas disponibles recientemente o episodios nuevos de programas de TV.

Carousel espera que, al menos, especifiques la cantidad de elementos que tiene el carrusel y cómo dibujar cada uno. El primero se puede especificar con itemCount. La segunda se puede pasar como lambda. El número de índice del elemento que se muestra se proporciona a la expresión lambda. Puedes determinar el elemento que se muestra con el valor de índice dado.

@Composable
function FeaturedCarousel(
  featuredContentList: List<Movie>,
  modifier: Modifier = Modifier,
) {
  Carousel(
    itemCount = featuredContentList.size,
    modifier = modifier,
  ) { index ->
    val content = featuredContentList[index]
    Box {
      AsyncImage(
        model = content.backgroundImageUrl,
        contentDescription = content.description,
        placeholder = painterResource(
          id = R.drawable.placeholder
        ),
        contentScale = ContentScale.Crop,
        modifier = Modifier.fillMaxSize()
      )
      Text(text = content.title)
    }
  }
}

Carousel puede ser un elemento de una lista diferida, como TvLazyColumn. En el siguiente fragmento, se muestra el elemento FeaturedCarousel componible sobre todos los elementos Section componibles.

@Composable
fun CatalogBrowser(
   featuredContentList: List<Movie>,
   sectionList: List<Section>,
   modifier: Modifier = Modifier,
   onItemSelected: (Movie) -> Unit = {},
) {
  TvLazyColumn(
    modifier = modifier.fillMaxSize(),
    verticalArrangement = Arrangement.spacedBy(16.dp)
  ) {

    item {
      FeaturedCarousel(featuredContentList)
    }

    items(sectionList) { section ->
      Section(section, onItemSelected = onItemSelected)
    }
  }
}