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
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
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 Composable
. Puedes encontrar la función Composable
CatalogBrowser
en el archivo CatalogBrowser.kt
. Implementarás la pantalla de navegador de catálogos en esta función Composable
.
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
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
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 collectAsState
. 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ónComposable
TvLazyColumn
a la funciónComposable
CatalogBrowser
. - Llama al método
catalogBrowserViewModel.categoryList.collectAsState()
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
Composable
Text
con el nombre de la categoría como el parámetro que se pasa como argumento de la lambda.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(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
Composable
TvLazyRow
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ónComposable
MovieCard
con un objetoMovie
.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow {
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ónComposable
TvLazyColumn
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ónComposable
TvLazyRow
con el parámetrohorizontalArrangement
. - Para establecer una sangría en la columna, pasa un objeto
PaddingValue
con el parámetrocontentPadding
.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
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 Composable
Details
en el archivo Details.kt
. Agregarás código a esta función para implementar la pantalla de detalles.
Details.kt
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.tv.material3.ExperimentalTvMaterial3Api
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@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
Composable
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
Composable
Text
para mostrar el título de la película. - Agrega una función
Composable
Text
para mostrar el nombre del estudio. - Agrega una función
Composable
Text
para mostrar la descripción de la película.
Details.kt
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@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.title)
}
}
En la siguiente tarea, usarás el objeto Modifier
especificado en el parámetro de la función Composable
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
Composable
Box
como un wrapper de la funciónComposable
Column
con el objetomodifier
pasado con la funciónComposable
Details
. - En la función
Composable
Box
, llama al métodofillMaxSize
del objetomodifier
para que la funciónComposable
Box
rellene el tamaño máximo que se puede asignar a la funciónComposable
Details
. - Agrega una función
Composable
AsyncImage
con los siguientes parámetros a la funciónComposable
Box
:
- 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
. - Establece un espacio vertical de 32 dp y un espacio horizontal de 48 dp en la columna a través de la configuración de un objeto
Modifier
que se creó llamando al métodoModifier.padding
.
Details.kt
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@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
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(
text = movie.title,
)
Text(
text = movie.studio,
)
Text(
text = movie.title,
)
}
}
}
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
][ColorScheme].
Para hacer referencia al objeto MaterialTheme
y así obtener temas coherentes, sigue estos pasos:
- Establece la propiedad
MaterialTheme.typography.headlineLarge
en el estilo de texto del título de la película. - Establece la propiedad
MaterialTheme.typography.headlineMedium
en el estilo de texto de las otras dos funcionesText
Composable
. - Establece la propiedad
MaterialTheme.colorScheme.background
en el color de fondo de la funciónComposable
Column
con el métodoModifier.background
.
[ColorScheme]: /reference/kotlin/androidx/tv/material3/ColorScheme)
Details.kt
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@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
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(
text = movie.title,
style = MaterialTheme.typography.headlineLarge,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.headlineMedium,
)
Text(
text = movie.title,
style = MaterialTheme.typography.headlineMedium,
)
}
}
}
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 Composable
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 Composable
MovieCard
como argumento.
- Abre el archivo
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Pasa una función lambda a la función
Composable
MovieCard
con un parámetroonClick
. - Llama a la devolución de llamada
onMovieSelected
con el objeto de película asociado con la funciónComposable
MovieCard
.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
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ónComposable
TvLazyColumn
. - 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
Composable
Carousel
dentro de la funciónitem
y, luego, pasa los siguientes parámetros:
- El tamaño de la variable
featuredMovieList
mediante un parámetroslideCount
- Un objeto
Modifier
para especificar el tamaño del carrusel con los métodosModifier.fillMaxWidth
yModifier.height
(la funciónComposable
Carousel
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
Composable
CarouselSlide
a la funciónComposable
Carousel
. - Agrega una función
Composable
Text
a la funciónComposable
CarouselSlide
para mostrar el título de la película.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp)
) { indexOfCarouselSlide ->
val featuredMovie =
featuredMovieList[indexOfCarouselSlide]
CarouselSlide {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
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 Composable
CarouselSlide
puede tomar otra lambda para especificar cómo se muestra el fondo de la función Composable
CarouselSlide
.
Para mostrar imágenes de fondo, sigue estos pasos:
- Pasa una lambda a la función
Composable
CarouselSlide
con el parámetrobackground
. - Llamar a la función
Composable
AsyncImage
para cargar la imagen de fondo asociada con el objetoMovie
en el fondo de la funciónComposable
CarouselSlide
. - Actualiza la posición y el estilo de texto de la función
Composable
Text
en la funciónComposable
CarouselSlide
para mejorar la visibilidad. - Establece un marcador de posición en la función
Composable
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
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.R
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
CarouselSlide(
background = {
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)
TvLazyRow(
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 permitir que los usuarios hagan clic en la función Composable
CarouselSlide
.
Para permitir que los usuarios vean los detalles de la película en el elemento visible del carrusel en la pantalla de detalles, sigue estos pasos:
- Pasa el valor que se muestra del método
Modifier.clickable
a la funciónComposable
CarouselSlide
a través del parámetromodifier
. - Llama a la función
onMovieSelected
con el objetoMovie
para la funciónComposable
CarouselSlide
visible en la expresión lambda pasada al métodoModifier.clickable
.
CatalogBrowser.kt
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.R
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
CarouselSlide(
background = {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
},
modifier = Modifier.clickable { onMovieSelected(featuredMovie) }
) {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
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 TvLazyColumn y TvLazyRow
- Cómo implementar una pantalla básica para mostrar detalles del contenido
- Cómo agregar transiciones de pantalla entre las dos pantallas