1. Antes de comenzar
Compose para TV es el framework de IU más reciente para desarrollar apps que se ejecutan en Android TV. Desbloquea todos los beneficios de Jetpack Compose para apps para TV, lo que facilita la compilación de IU atractivas y funcionales para tu app. Estos son algunos de los beneficios específicos de Compose para TV:
- Flexibilidad: Compose se puede usar para crear cualquier tipo de IU, desde diseños simples hasta animaciones complejas. Los componentes funcionan de inmediato, pero también se pueden personalizar y adaptar a las necesidades de tu app.
- Desarrollo simplificado y acelerado: Compose es compatible con el código existente y permite que los desarrolladores compilen apps con menos código.
- Intuición: Compose usa una sintaxis declarativa que le permite cambiar tu IU de forma intuitiva, así como depurar, comprender y revisar el código.
Un caso de uso común para las apps para TV es el consumo de contenido multimedia. Los usuarios exploran los catálogos de contenido y seleccionan el contenido que quieren mirar. Ese contenido puede ser una película, un programa de TV o un podcast. Después de que los usuarios seleccionen contenido, es posible que quieran ver más información sobre este, como una descripción breve, la duración de la reproducción y los nombres de los creadores. En este codelab, aprenderás a implementar una pantalla de navegador de catálogos y una pantalla de detalles con Compose para TV.
Requisitos previos
- Tener experiencia con la sintaxis de Kotlin, incluidas las funciones de lambdas
- Tener experiencia básica con Compose (si no estás familiarizado con Compose, completa el codelab Conceptos básicos de Jetpack Compose)
- Tener conocimientos básicos de elementos componibles y modificadores
- Cualquiera de los siguientes dispositivos para ejecutar la app de ejemplo:
- Un dispositivo Android TV
- Un dispositivo virtual de Android con un perfil en la categoría de definición de dispositivo de TV
Qué compilarás
- Una app de reproducción de video con una pantalla de navegador de catálogos y una pantalla de detalles
- Una pantalla de navegador de catálogos que muestra una lista de videos para que los usuarios elijan; se ve como la siguiente imagen:
- Una pantalla de detalles que muestra los metadatos de un video seleccionado, como el título, la descripción y la duración; se ve como la siguiente imagen:
Requisitos
- La versión más reciente de Android Studio
- Un dispositivo Android TV o un dispositivo virtual en la categoría de dispositivos de TV
2. Prepárate
Para obtener el código que contiene los temas y la configuración básica de este codelab, realiza una de las siguientes acciones:
- Clona el código de este repositorio de GitHub:
$ git clone https://github.com/android/tv-codelabs.git
La rama main
contiene el código de partida, y la rama solution
incluye el código de la solución.
- Descarga el archivo
main.zip
, que contiene el código de partida, y el archivosolution.zip
, que contiene el código de la solución.
Ahora que descargaste el código, abre la carpeta del proyecto IntroductionToComposeForTV en Android Studio. Ya está todo listo para comenzar.
3. Implementa la pantalla del navegador de catálogos
La pantalla del navegador de catálogos les permite a los usuarios explorar catálogos de películas. Implementarás el navegador de catálogos como una función de componibilidad. Encontrarás la función de componibilidad CatalogBrowser
en el archivo CatalogBrowser.kt
. Implementarás la pantalla de navegador de catálogos en esta función de componibilidad.
El código de partida tiene un ViewModel llamado clase CatalogBrowserViewModel
que tiene varios atributos y métodos para recuperar objetos Movie
que describen contenido de películas. Implementarás un navegador de catálogos con objetos Movie
recuperados.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
Cómo mostrar los nombres de las categorías
Puedes acceder a una lista de categorías con el atributo catalogBrowserViewModel.categoryList
, que es un flujo de una lista de Category
. El flujo se recopila como un objeto State
de Compose llamando a su método collectAsStateWithLifecycle
. Un objeto Category
tiene el atributo name
, que es un valor String
que representa el nombre de la categoría.
Para mostrar los nombres de las categorías, sigue estos pasos:
- En Android Studio, abre el archivo
CatalogBrowser.kt
del código de partida y, luego, agrega una función de componibilidadLazyColumn
a la función de componibilidadCatalogBrowser
. - Llama al método
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
para recopilar el flujo como un objetoState
. - Declara
categoryList
como una propiedad delegada del objetoState
que creaste en el paso anterior. - Llama a la función
items
con la variablecategoryList
como parámetro. - Llama a la función de componibilidad
Text
con el nombre de la categoría como el parámetro que se pasa como argumento de la expresión lambda.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
}
}
}
Cómo mostrar la lista de contenido de cada categoría
Un objeto Category
tiene otro atributo llamado movieList
. El atributo es una lista de objetos Movie
que representan películas que pertenecen a la categoría.
Para mostrar la lista de contenido de cada categoría, sigue estos pasos:
- Agrega la función de componibilidad
LazyRow
y, luego, pasa una lambda a ella. - En la expresión lambda, llama a la función
items
con el valor del atributocategory
.movieList
y, luego, pasa una lambda a ella. - En la lambda pasada a la función
items
, llama a la función de componibilidadMovieCard
con un objetoMovie
.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
LazyRow {
items(category.movieList) {movie ->
MovieCard(movie = movie)
}
}
}
}
}
Cómo ajustar el diseño (opcional)
- Para establecer la brecha entre las categorías, pasa un objeto
Arrangement
a la función de componibilidadLazyColumn
con el parámetroverticalArrangement
. El objetoArrangement
se crea llamando al métodoArrangement#spacedBy
. - Para establecer la brecha entre las tarjetas de películas, pasa un objeto
Arrangement
a la función de componibilidadLazyRow
con el parámetrohorizontalArrangement
. - Para establecer una sangría en la columna, pasa un objeto
PaddingValue
con el parámetrocontentPadding
.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie)
}
}
}
}
}
4. Implementa la pantalla de detalles
La pantalla de detalles muestra los detalles de la película seleccionada. Hay una función de componibilidad Details
en el archivo Details.kt
. Agregarás código a esta función para implementar la pantalla de detalles.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
Cómo mostrar el título de la película, el nombre del estudio y la descripción
Un objeto Movie
tiene los siguientes tres atributos de cadena como metadatos de la película:
title
es el título de la película.studio
es el nombre del estudio que produjo la película.description
es un breve resumen de la película.
Para mostrar estos metadatos en la pantalla de detalles, sigue estos pasos:
- Agrega una función de componibilidad
Column
y, luego, establece el espacio horizontal y vertical alrededor de la columna en 48 dp y 32 dp, respectivamente, con el objetoModifier
creado por el métodoModifier.padding
. - Agrega una función de componibilidad
Text
para mostrar el título de la película. - Agrega una función de componibilidad
Text
para mostrar el nombre del estudio. - Agrega una función de componibilidad
Text
para mostrar la descripción de la película.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(text = movie.title)
Text(text = movie.studio)
Text(text = movie.description)
}
}
En la siguiente tarea, usarás el objeto Modifier
especificado en el parámetro de la función de componibilidad Details
.
Cómo mostrar la imagen de fondo asociada a un objeto Movie
determinado.
Un objeto Movie
tiene un atributo backgroundImageUrl
que indica la ubicación de la imagen de fondo de la película que describe el objeto.
Para mostrar la imagen de fondo de una película determinada, sigue estos pasos:
- Agrega una función de componibilidad
Box
como wrapper de la función de componibilidadColumn
con el objetomodifier
pasado a través de la función de componibilidadDetails
. - En la función de componibilidad
Box
, llama al métodofillMaxSize
del objetomodifier
para que la función de componibilidadBox
rellene el tamaño máximo que se puede asignar a la función de componibilidadDetails
. - Agrega una función de componibilidad
AsyncImage
con los siguientes parámetros a la función de componibilidadBox
:
- Establece el valor del atributo
backgroundImageUrl
del objetoMovie
determinado en un parámetromodel
. - Pasa
null
a un parámetrocontentDescription
.
- Pasa un objeto
ContentScale.Crop
a un parámetrocontentScale
. Para ver las diferentes opciones deContentScale
, consulta Escala de contenido. - Pasa el valor que se muestra del método
Modifier.fillMaxSize
al parámetromodifier
.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column {
Text(
text = movie.title,
)
Text(
text = movie.studio,
)
Text(text = movie.description)
}
}
}
Cómo hacer referencia al objeto MaterialTheme
para obtener temas coherentes
El objeto MaterialTheme
contiene funciones que hacen referencia a los valores de tema actuales, como los de las clases Typography
y ColorScheme
.
Para hacer referencia al objeto MaterialTheme
y así obtener temas coherentes, sigue estos pasos:
- Establece la propiedad
MaterialTheme.typography.displayMedium
en el estilo de texto del título de la película. - Configura la propiedad
MaterialTheme.typography.bodySmall
en el estilo de texto de las segundas funciones de componibilidadText
. - Establece la propiedad
MaterialTheme.colorScheme.background
en el color de fondo de la función de componibilidadColumn
con el métodoModifier.background
.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.background),
) {
Text(
text = movie.title,
style = MaterialTheme.typography.displayMedium,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.bodySmall,
)
Text(text = movie.description)
}
}
}
Cómo ajustar el diseño (opcional)
Para ajustar el diseño de la función de componibilidad Details
, sigue estos pasos:
- Configura la función de componibilidad
Box
para usar todo el espacio disponible con el modificadorfillMaxSize
. - Configura el fondo de la función de componibilidad
Box
con el modificadorbackground
para rellenar el fondo con un gradiente lineal que se crea llamando a la funciónBrush.linearGradient
con una lista de objetosColor
que contienen el valorMaterialTheme.colorScheme.background
yColor.Transparent
. - Configura el espacio horizontal
48.dp
y vertical24.dp
alrededor de la función de componibilidadColumn
con el modificadorpadding
. - Configura el ancho de la función de componibilidad
Column
con el modificadorwidth
que se crea llamando a la funciónModifier.width
con el valor0.5f
. - Agrega el espacio
8.dp
entre la segunda función de componibilidadText
y el tercer elemento componibleText
conSpacer
. La altura de la función de componibilidadSpacer
se especifica con el modificadorheight
que se crea con la funciónModifier.height
.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.background(
Brush.linearGradient(
listOf(
MaterialTheme.colorScheme.background,
Color.Transparent
)
)
)
.fillMaxSize()
) {
Column(
modifier = Modifier
.padding(horizontal = 48.dp, vertical = 24.dp)
.fillMaxWidth(0.5f)
) {
Text(
text = movie.title,
style = MaterialTheme.typography.displayMedium,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.bodySmall,
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = movie.description,
)
}
}
}
}
5. Agrega navegación entre las pantallas
Ya cuentas con las pantallas de detalles y del navegador de catálogos. Después de que un usuario selecciona contenido en la pantalla del navegador de catálogos, la pantalla debe pasar a la de detalles. Para que esto sea posible, usa el modificador clickable
para agregar un objeto de escucha event
a la función de componibilidad MovieCard
. Cuando se presiona el botón central del mando de dirección, se llama al método CatalogBrowserViewModel#showDetails
con el objeto de película asociado con la función de componibilidad MovieCard
como argumento.
- Abre el archivo
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Pasa una función lambda a la función de componibilidad
MovieCard
con un parámetroonClick
. - Llama a la devolución de llamada
onMovieSelected
con el objeto de película asociado con la función de componibilidadMovieCard
.
CatalogBrowser.kt
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
6. Agrega un carrusel a la pantalla del navegador de catálogos para mostrar contenido destacado
El carrusel es un componente de IU de adaptación común que actualiza automáticamente sus diapositivas al cabo de una duración específica. Por lo general, se usa para mostrar contenido destacado.
Si quieres agregar un carrusel a la pantalla del navegador de catálogos para mostrar películas en la lista de contenido destacado, sigue estos pasos:
- Abre el archivo
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Llama a la función
item
para agregar un elemento a la función de componibilidadLazyColumn
. - Declara
featuredMovieList
como una propiedad delegada en la expresión lambda pasada a la funciónitem
y, luego, configura el objetoState
que se delegará, que se recopilará del atributocatalogBrowserViewModel.featuredMovieList
. - Llama a la función de componibilidad
Carousel
dentro de la funciónitem
y, luego, pasa los siguientes parámetros:
- El tamaño de la variable
featuredMovieList
a través de un parámetroslideCount
. - Un objeto
Modifier
para especificar el tamaño del carrusel con los métodosModifier.fillMaxWidth
yModifier.height
. La función de componibilidadCarousel
usa 376 dp de altura pasando un valor376.dp
al métodoModifier.height
. - Una lambda llamada con un valor de número entero que indica el índice del elemento visible del carrusel.
- Recupera el objeto
Movie
de la variablefeaturedMovieList
y el valor del índice determinado. - Agrega una función de componibilidad
Box
a la función de componibilidadCarousel
. - Agrega una función de componibilidad
Text
a la función de componibilidadBox
para mostrar el título de la película.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp)
) { indexOfCarouselSlide ->
val featuredMovie =
featuredMovieList[indexOfCarouselSlide]
Box {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Cómo mostrar imágenes de fondo
La función de componibilidad Box
coloca un componente sobre otro. Consulta los Conceptos básicos de diseño para obtener más detalles.
Para mostrar imágenes de fondo, sigue estos pasos:
- Llama a la función de componibilidad
AsyncImage
para cargar la imagen de fondo asociada con el objetoMovie
antes de la función de componibilidadText
. - Actualiza la posición y el estilo de texto de la función de componibilidad
Text
para mejorar la visibilidad. - Configura un marcador de posición en la función de componibilidad
AsyncImage
para evitar el cambio de diseño. El código de partida tiene un marcador de posición como elemento de diseño al que puedes hacer referencia conR.drawable.placeholder
.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
Box{
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Cómo agregar una transición de pantallas a la pantalla de detalles
Puedes agregar un objeto Button
al carrusel para que los usuarios puedan hacer clic en el botón para activar una transición de pantalla a la pantalla de detalles.
Para permitir que los usuarios vean los detalles de la película en el carrusel visible en la pantalla de detalles, sigue estos pasos:
- Llama a la función de componibilidad
Column
en el elementoBox
componible, en el elemento componibleCarousel
. - Mueve el elemento componible
Text
en el elementoCarousel
a la función de componibilidadColumn
. - Llama a la función de componibilidad
Button
después de la función de componibilidadText
en la función de componibilidadColumn
. - Llama a la función de componibilidad
Text
en la función de componibilidadButton
con el valor que se muestra de la funciónstringResource
a la que se llamó conR.string.show_details
. - Llama a la función
onMovieSelected
con la variablefeaturedMovie
en la expresión lambda pasada al parámetroonClick
de la función de componibilidadButton
.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
Box {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
Column {
Text(text = featuredMovie.title)
Button(onClick = { onMovieSelected(featuredMovie) }) {
Text(text = stringResource(id = R.string.show_details))
}
}
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Cómo ajustar el diseño (opcional)
Para ajustar el diseño del carrusel, sigue estos pasos:
- Asigna el valor
backgroundColor
con el valorMaterialTheme.colorScheme.background
en la función de componibilidadCarousel
. - Une la función de componibilidad
Column
con un elemento componibleBox
. - Pasa el valor
Alignment.BottomStart
al parámetrocontentAlignment
del componenteBox
. - Pasa el modificador
fillMaxSize
al parámetro modificador de la función de componibilidadBox
. El modificadorfillMaxSize
se crea con la funciónModifier.fillMaxSize()
. - Llama al método
drawBehind()
mediante el modificadorfillMaxSize
que se pasa al elemento componibleBox
. - En la lambda pasada al modificador
drawBehind
, asigna el valorbrush
con un objetoBrush
que se crea llamando a la funciónBrush.linearGradient
con una lista de dos objetosColor
. La lista se crea llamando a la funciónlistOf
con el valorbackgroundColor
y el valorColor.Transparent
. - Llama a
drawRect
con el objetobrush
de la expresión lambda pasada al modificadordrawBehind
para crear una capa srim sobre la imagen de fondo. - Especifica el relleno de la función de componibilidad
Column
con el modificadorpadding
que se crea llamando aModifier.padding
con el valor20.dp
. - Agrega una función de componibilidad
Spacer
con el valor20.dp
entre el elemento componibleText
y el elemento componibleButton
en la función de componibilidadColumn
.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(32.dp),
contentPadding = PaddingValues(horizontal = 58.dp, vertical = 36.dp)
) {
item {
val featuredMovieList by
catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
Carousel(
itemCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
val backgroundColor = MaterialTheme.colorScheme.background
Box {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
Box(
contentAlignment = Alignment.BottomStart,
modifier = Modifier
.fillMaxSize()
.drawBehind {
val brush = Brush.horizontalGradient(
listOf(backgroundColor, Color.Transparent)
)
drawRect(brush)
}
) {
Column(
modifier = Modifier.padding(20.dp)
) {
Text(
text = featuredMovie.title,
style = MaterialTheme.typography.displaySmall
)
Spacer(modifier = Modifier.height(28.dp))
Button(onClick = { onMovieSelected(featuredMovie) }) {
Text(text = stringResource(id = R.string.show_details))
}
}
}
}
}
}
items(categoryList) { category ->
Text(text = category.name)
LazyRow(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.height(200.dp)
) {
items(category.movieList) { movie ->
MovieCard(
movie,
onClick = {
onMovieSelected(it)
}
)
}
}
}
}
}
7. Obtén el código de la solución
Para descargar el código de la solución de este codelab, realiza una de las siguientes acciones:
- Haz clic en el siguiente botón para descargarlo como un archivo ZIP, luego descomprímelo y ábrelo en Android Studio.
- Recupéralo con Git:
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
8. Felicitaciones.
¡Felicitaciones! Aprendiste los conceptos básicos de Compose para TV:
- Cómo implementar una pantalla para mostrar una lista de contenido combinando LazyColumn y LazyLow
- Cómo implementar una pantalla básica para mostrar detalles del contenido
- Cómo agregar transiciones de pantalla entre las dos pantallas