1. Antes de começar
Compose para TV é o framework de interface mais recente para desenvolver apps executados no Android TV. Ele oferece todos os benefícios do Jetpack Compose para apps de TV, o que facilita a criação de interfaces incríveis e funcionais para seu app. Confira alguns benefícios específicos do Compose para TV:
- Flexibilidade. O Compose pode ser usado para criar qualquer tipo de interface, desde layouts simples a animações complexas. Os componentes funcionam imediatamente, mas também podem ser personalizados e estilizados para atender às necessidades do seu app.
- Desenvolvimento simplificado e acelerado. O Compose é compatível com códigos já existentes e permite que os desenvolvedores criem apps com menos código.
- Intuição: o Compose usa uma sintaxe declarativa que torna intuitivas a mudança da interface e a depuração, compreensão e revisão do código.
Um caso de uso comum para apps de TV é o consumo de mídia. Os usuários navegam por catálogos de conteúdo e selecionam aquele que querem assistir. O conteúdo pode ser um filme, um programa de TV ou um podcast. Depois de selecionar um conteúdo, os usuários podem conferir mais informações sobre ele, por exemplo, uma descrição curta, a duração da reprodução e o nome dos criadores. Neste codelab, você vai aprender a implementar uma tela para um navegador de catálogo e uma tela de detalhes com o Compose para TV.
Pré-requisitos
- Experiência com a sintaxe do Kotlin, incluindo lambdas.
- Experiência básica com o Compose. Se você não conhece o Compose, conclua o codelab Noções básicas do Jetpack Compose.
- Conhecimento básico de combináveis e modificadores.
- Qualquer um destes dispositivos para executar o app de exemplo:
- Um dispositivo Android TV
- Um dispositivo virtual Android com um perfil na categoria de definição de dispositivo de TV
O que você vai criar
- Um app de player de vídeo com uma tela de navegador de catálogo e uma tela de detalhes.
- Uma tela de navegador de catálogo que mostra uma lista de vídeos para os usuários escolherem. Ela tem a seguinte aparência:
- Uma tela de detalhes que mostra os metadados de um vídeo selecionado, por exemplo, título, descrição e duração. Ela tem a seguinte aparência:
O que é necessário
- A versão mais recente do Android Studio
- Um dispositivo Android TV ou virtual na categoria de dispositivo de TV
2. Começar a configuração
Para receber o código que contém a configuração básica e de aplicação de temas para este codelab, siga um destes procedimentos:
- Clone o código deste repositório do GitHub:
$ git clone https://github.com/android/tv-codelabs.git
A ramificação main
contém o código inicial, e a ramificação solution
contém o código da solução.
- Faça o download do arquivo
main.zip
, que contém o código inicial, e do arquivosolution.zip
, que contém o código da solução.
Depois de fazer o download do código, abra a pasta do projeto IntroductionToComposeForTV no Android Studio. Está tudo pronto para começar.
3. Implementar a tela do navegador de catálogo
A tela do navegador de catálogo permite que os usuários procurem catálogos de filmes. Implemente a tela do navegador de catálogo como uma função combinável. A função combinável CatalogBrowser
está no arquivo CatalogBrowser.kt
. Implemente a tela do navegador de catálogo nesta função combinável.
O código inicial tem um ViewModel conhecido como a classe CatalogBrowserViewModel
que tem vários atributos e métodos para extrair objetos Movie
que descrevem o conteúdo do filme. Você implementa um navegador de catálogo com objetos Movie
recuperados.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
Mostrar os nomes das categorias
É possível acessar uma lista de categorias com o atributo catalogBrowserViewModel.categoryList
, que é um fluxo de uma lista de categorias (Category
). O fluxo é coletado como um objeto Compose State
chamando o método collectAsStateWithLifecycle
dele. Um objeto Category
tem o atributo name
, que é um valor String
que representa o nome da categoria.
Para mostrar os nomes das categorias, siga estas etapas:
- No Android Studio, abra o arquivo
CatalogBrowser.kt
do código inicial e adicione uma função combinávelLazyColumn
à função combinávelCatalogBrowser
. - Chame o método
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
para coletar o fluxo como um objetoState
. - Declare
categoryList
como uma propriedade delegada do objetoState
que você criou na etapa anterior. - Chame a função
items
com a variávelcategoryList
como parâmetro. - Chame a função combinável
Text
com o nome da categoria como o parâmetro transmitido como um argumento da 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)
}
}
}
Mostrar a lista de conteúdo de cada categoria
Um objeto Category
tem outro atributo chamado movieList
. O atributo é uma lista de objetos Movie
que representam os filmes que pertencem à categoria.
Para mostrar a lista de conteúdos de cada categoria, siga estas etapas:
- Adicione a função combinável
LazyRow
e transmita uma lambda a ela. - Na lambda, chame a função
items
com o valor de atributocategory
.movieList
e, em seguida, transmita uma lambda a ela. - Na lambda transmitida à função
items
, chame a função combinávelMovieCard
com um 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)
}
}
}
}
}
Opcional: ajustar o layout
- Para definir a lacuna entre as categorias, transmita um objeto
Arrangement
à função combinávelLazyColumn
com o parâmetroverticalArrangement
. O objetoArrangement
é criado chamando o métodoArrangement#spacedBy
. - Para definir a lacuna entre os cartões de filmes, transmita um objeto
Arrangement
à função combinávelLazyRow
com o parâmetrohorizontalArrangement
. - Para definir um recuo na coluna, transmita um objeto
PaddingValue
com o 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. Implementar a tela de detalhes
A tela de detalhes mostra os detalhes do filme selecionado. Há uma função combinável Details
no arquivo Details.kt
. Adicione o código a essa função para implementar a tela de detalhes.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
Mostrar o título, o nome do estúdio e a descrição do filme
Um objeto Movie
tem estes três atributos de string como metadados do filme:
title
: o título do filme.studio
: o nome do estúdio que produziu o filme.description
: um breve resumo do filme.
Para mostrar esses metadados na tela de detalhes, siga estas etapas:
- Adicione uma função combinável
Column
e defina a área livre vertical como 32 dp e a horizontal como 48 dp ao redor da coluna com o objetoModifier
criado pelo métodoModifier.padding
. - Adicione uma função combinável
Text
para mostrar o título do filme. - Adicione uma função combinável
Text
para mostrar o nome do estúdio. - Adicione uma função combinável
Text
para mostrar a descrição do filme.
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)
}
}
O objeto Modifier
especificado no parâmetro da função combinável Details
é usado na próxima tarefa.
Mostrar a imagem de plano de fundo associada a um determinado objeto Movie
Um objeto Movie
tem um atributo backgroundImageUrl
que indica o local da imagem de plano de fundo do filme descrito pelo objeto.
Para mostrar a imagem de plano de fundo de um determinado filme, siga estas etapas:
- Adicione uma função combinável
Box
como um wrapper da função combinávelColumn
com o objetomodifier
transmitido pela função combinávelDetails
. - Na função combinável
Box
, chame o métodofillMaxSize
do objetomodifier
para que a função combinávelBox
preencha o tamanho máximo que pode ser alocado para a função combinávelDetails
. - Adicione uma função combinável
AsyncImage
à funçãoBox
com os parâmetros abaixo:
- Define o valor do atributo
backgroundImageUrl
do objetoMovie
especificado como um parâmetromodel
. - Transmita
null
a um parâmetrocontentDescription
.
- Transmita um objeto
ContentScale.Crop
a um parâmetrocontentScale
. Para conferir as diferentes opções deContentScale
, consulte Escala de conteúdo. - Transmita o valor de retorno do método
Modifier.fillMaxSize
ao 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)
}
}
}
Consultar o objeto MaterialTheme
para obter uma aplicação consistente de temas
O objeto MaterialTheme
contém funções para indicar valores de tema atuais, por exemplo, os das classes Typography
e ColorScheme
.
Para consultar o objeto MaterialTheme
e ter uma aplicação consistente de temas, siga estas etapas:
- Defina a propriedade
MaterialTheme.typography.displayMedium
como o estilo de texto do título do filme. - Defina a propriedade
MaterialTheme.typography.bodySmall
como o estilo de texto da segunda função combinávelText
. - Defina a propriedade
MaterialTheme.colorScheme.background
como a cor de fundo da funçãoColumn
com o 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)
}
}
}
Opcional: ajustar o layout
Para ajustar o layout da função combinável Details
, siga estas etapas:
- Defina a função combinável
Box
para usar todo o espaço disponível com o modificadorfillMaxSize
. - Defina o segundo plano da função combinável
Box
com o modificadorbackground
para preencher o segundo plano com um gradiente linear criado ao chamar a funçãoBrush.linearGradient
com uma lista de objetosColor
contendo o valorMaterialTheme.colorScheme.background
eColor.Transparent
- Defina a área livre horizontal
48.dp
e vertical24.dp
ao redor da função combinávelColumn
com o modificadorpadding
- Defina a largura da função combinável
Column
com o modificadorwidth
, que é criado chamando a funçãoModifier.width
com o valor0.5f
. - Adicione o espaçamento
8.dp
entre a segunda função combinávelText
e o terceiro combinávelText
usandoSpacer
. A altura da função combinávelSpacer
é especificada com o modificadorheight
, que é criado com a funçãoModifier.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. Adicionar navegação entre as telas
Agora você tem a tela do navegador de catálogo e as telas de detalhes. Depois que um usuário seleciona o conteúdo na tela do navegador de catálogo, é necessário mudar para a tela de detalhes. Para que isso seja possível, use o modificador clickable
para adicionar um listener event
à função combinável MovieCard
. Quando o botão central do botão direcional é pressionado, o método CatalogBrowserViewModel#showDetails
é chamado, apresentando o objeto do filme associado à função combinável MovieCard
como um argumento.
- Abra o arquivo
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Transmita uma função lambda à função combinável
MovieCard
com um parâmetroonClick
. - Chame o callback
onMovieSelected
com o objeto do filme associado à função combinávelMovieCard
.
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. Adicionar um carrossel à tela do navegador de catálogo para realçar o conteúdo em destaque
O carrossel é um componente da interface comumente adaptado que atualiza automaticamente os slides após uma duração específica. É usado normalmente para realçar o conteúdo em destaque.
Para adicionar um carrossel à tela do navegador de catálogo e realçar filmes na lista de conteúdo em destaque, siga estas etapas:
- Abra o arquivo
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Chame a função
item
para adicionar um item à função combinávelLazyColumn
. - Declare
featuredMovieList
como uma propriedade delegada na lambda transmitida à funçãoitem
e defina o objetoState
a ser delegado, que é coletado do atributocatalogBrowserViewModel.featuredMovieList
. - Chame a função combinável
Carousel
dentro da funçãoitem
e transmita estes parâmetros:
- O tamanho da variável
featuredMovieList
por um parâmetroslideCount
. - Um objeto
Modifier
para especificar o tamanho do carrossel com os métodosModifier.fillMaxWidth
eModifier.height
. A função combinávelCarousel
usa 376 dp de altura, transmitindo um valor376.dp
ao métodoModifier.height
. - Uma lambda chamada com um valor inteiro que indica o índice do item do carrossel visível.
- Extraia o objeto
Movie
da variávelfeaturedMovieList
e do valor de índice fornecido. - Adicione uma função combinável
Box
à função combinávelCarousel
. - Adicione uma função combinável
Text
à função combinávelBox
para mostrar o título do filme.
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) })
}
}
}
}
}
Mostrar imagens de plano de fundo
A função combinável Box
coloca um componente em cima de outro. Consulte Conceitos básicos de layout para mais detalhes.
Para mostrar imagens de plano de fundo, siga estas etapas:
- Chame a função combinável
AsyncImage
para carregar a imagem de plano de fundo associada ao objetoMovie
antes da função combinávelText
. - Atualize a posição e o estilo do texto da função combinável
Text
para melhorar a visibilidade. - Defina um marcador de posição para a função combinável
AsyncImage
com o objetivo de evitar a mudança de layout. O código inicial tem um marcador de posição como um drawable que pode ser referenciado com oR.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) })
}
}
}
}
}
Adicionar uma transição de telas à tela de detalhes
É possível adicionar um Button
ao carrossel para que os usuários possam acionar uma transição para a tela de detalhes clicando no botão.
Para que os usuários possam conferir os detalhes do filme no carrossel visível na tela de detalhes, siga estas etapas:
- Chame a função combinável
Column
no elemento combinávelBox
no combinávelCarousel
- Mova o elemento combinável
Text
noCarousel
para a função combinávelColumn
. - Chame a função combinável
Button
depois da funçãoText
na funçãoColumn
. - Chame a função combinável
Text
na funçãoButton
com o valor de retorno da funçãostringResource
, chamada comR.string.show_details
. - Chame a função
onMovieSelected
com a variávelfeaturedMovie
na lambda transmitida ao parâmetroonClick
da função combinávelButton
.
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) })
}
}
}
}
}
Opcional: ajustar o layout
Para ajustar o layout do carrossel, siga estas etapas:
- Atribua o valor
backgroundColor
com o valorMaterialTheme.colorScheme.background
na função combinávelCarousel
. - Unir a função combinável
Column
a um elemento combinávelBox
- Transmita o valor
Alignment.BottomStart
ao parâmetrocontentAlignment
do componenteBox
. - Transmita o modificador
fillMaxSize
ao parâmetro modificador da função combinávelBox
. O modificadorfillMaxSize
é criado com a funçãoModifier.fillMaxSize()
. - Chame o método
drawBehind()
no modificadorfillMaxSize
transmitido ao elemento combinávelBox
. - Na lambda transmitida ao modificador
drawBehind
, atribua o valorbrush
com um objetoBrush
que é criado ao chamar a funçãoBrush.linearGradient
com uma lista de dois objetosColor
. A lista é criada chamando a funçãolistOf
com os valoresbackgroundColor
eColor.Transparent
. - Chame
drawRect
com o objetobrush
na lambda transmitida ao modificadordrawBehind
para criar uma camada srim sobre a imagem de plano de fundo - Especifique o padding da função combinável
Column
com o modificadorpadding
, que é criado chamandoModifier.padding
com o valor20.dp
. - Adicione uma função combinável
Spacer
com o valor20.dp
entre os elementos combináveisText
eButton
na função combinávelColumn
.
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. Acessar o código da solução
Para fazer o download do código da solução para este codelab, realize uma destas ações:
- Clique no botão a seguir para fazer o download como um arquivo ZIP. Em seguida, descompacte e abra esse arquivo no Android Studio.
- Recupere-o com o Git:
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
8. Parabéns!
Parabéns! Você aprendeu as noções básicas do Compose para TV:
- Como implementar uma tela para mostrar uma lista de conteúdo combinando LazyColumn e LazyLow.
- A implementação básica de tela para mostrar detalhes do conteúdo.
- Como adicionar transições entre as duas telas.