1. Avant de commencer
Compose pour la télévision est le dernier framework d'UI permettant de développer des applications exécutées sur Android TV. Il offre tous les avantages de Jetpack Compose pour les applis TV afin de vous permettre de créer plus facilement des interfaces utilisateur attrayantes et fonctionnelles. Voici quelques avantages spécifiques de Compose pour la télévision :
- Flexibilité : Compose permet de créer n'importe quel type d'UI, qu'il s'agisse de mises en page simples ou d'animations complexes. Les composants sont prêts à l'emploi, mais vous pouvez aussi les personnaliser et les façons selon les besoins de votre application.
- Développement accéléré et simplifié : Compose étant compatible avec le code existant, les développeurs peuvent créer des applications avec moins de code.
- Intuitivité : Compose utilise une syntaxe déclarative qui permet de modifier votre UI de manière intuitive, mais aussi de déboguer, comprendre et examiner votre code.
Parmi les cas d'utilisation courants des applications TV, on retrouve la consommation de contenus multimédias. Les utilisateurs parcourent les catalogues de contenus et sélectionnent ceux qu'ils souhaitent regarder. Il peut s'agir d'un film, d'une série TV ou d'un podcast. Après avoir sélectionné un contenu, l'utilisateur peut souhaiter en savoir plus sur celui-ci, par exemple par le biais d'une brève description, de la durée de la lecture et du nom des créateurs. Dans cet atelier de programmation, vous découvrirez comment implémenter un navigateur de catalogue et un écran d'informations avec Compose pour la télévision.
Conditions préalables
- Connaissances de la syntaxe du langage Kotlin, y compris les lambdas.
- Vous disposez d'une expérience de base avec Compose. Si vous ne connaissez pas Compose, suivez l'atelier de programmation Principes de base de Jetpack Compose.
- Vous disposez de connaissances de base sur les composables et les modificateurs.
- N'importe lequel des appareils suivants devra exécuter l'application exemple :
- Un appareil Android TV
- Un appareil virtuel Android avec un profil dans la catégorie de définition des appareils TV
Objectif de l'atelier
- Une application de lecteur vidéo avec un navigateur de catalogue et un écran d'informations
- Un navigateur de catalogue affichant une liste de vidéos que les utilisateurs peuvent choisir. Il se présente comme suit :
- Un écran d'informations affichant les métadonnées de la vidéo sélectionnée, comme le titre, la description et la durée. Il se présente comme suit :
Ce dont vous avez besoin
- La dernière version d'Android Studio
- Un appareil Android TV ou un appareil virtuel dans la catégorie "Téléviseur"
2. Configuration
Afin d'obtenir le code contenant la thématisation et la configuration de base pour cet atelier de programmation, effectuez l'une des opérations suivantes :
- Clonez le code de ce dépôt GitHub :
$ git clone https://github.com/android/tv-codelabs.git
La branche main
contient le code de démarrage et la branche solution
contient le code de solution.
- Téléchargez le fichier
main.zip
, qui contient le code de démarrage, et le fichiersolution.zip
, qui contient le code de solution.
Maintenant que vous avez téléchargé le code, ouvrez le projet IntroductionToComposeForTV dans Android Studio. Vous êtes prêt à commencer.
3. Implémenter l'écran du navigateur de catalogue
Grâce au navigateur, les utilisateurs peuvent parcourir les catalogues de films. Vous allez implémenter le navigateur de catalogue en tant que fonction composable. La fonction composable CatalogBrowser
se trouve dans le fichier CatalogBrowser.kt
. Vous allez implémenter le navigateur de catalogue dans cette fonction composable.
Le code de démarrage contient une classe ViewModel appelée CatalogBrowserViewModel
. Elle comporte plusieurs attributs et méthodes pour récupérer des objets Movie
décrivant le contenu du film. Vous allez implémenter un navigateur de catalogue en récupérant des objets Movie
.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
Afficher les noms des catégories
Vous pouvez accéder à une liste de catégories avec l'attribut catalogBrowserViewModel.categoryList
. il s'agit du flux d'une liste Category
. Le flux est collecté en tant qu'objet Compose State
en appelant la méthode collectAsStateWithLifecycle
. Un objet Category
possède l'attribut name
. Il s'agit d'une valeur String
correspondant au nom de la catégorie.
Pour afficher les noms des catégories, procédez comme suit :
- Dans Android Studio, ouvrez le fichier
CatalogBrowser.kt
du code de démarrage, puis ajoutez une fonction composableLazyColumn
à la fonction composableCatalogBrowser
. - Appelez la méthode
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
pour collecter le flux en tant qu'objetState
. - Déclarez
categoryList
en tant que propriété déléguée de l'objetState
créé à l'étape précédente. - Appelez la fonction
items
avec la variablecategoryList
en tant que paramètre. - Appelez la fonction composable
Text
avec le nom de la catégorie comme paramètre transmis en tant qu'argument du 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)
}
}
}
Afficher la liste de contenu pour chaque catégorie
Un objet Category
possède un autre attribut appelé movieList
. Il s'agit d'une liste d'objets Movie
correspondant aux films qui appartiennent à la catégorie.
Pour afficher la liste de contenu de chaque catégorie, procédez comme suit :
- Ajoutez la fonction composable
LazyRow
, puis transmettez-lui un lambda. - Dans le lambda, appelez la fonction
items
aveccategory
et la valeur d'attributmovieList
, puis transmettez-lui un lambda. - Dans le lambda transmis à la fonction
items
, appelez la fonction composableMovieCard
avec un objetMovie
.
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)
}
}
}
}
}
Facultatif : ajuster la mise en page
- Pour définir l'écart entre les catégories, transmettez un objet
Arrangement
à la fonction composableLazyColumn
avec le paramètreverticalArrangement
. L'objetArrangement
est créé en appelant la méthodeArrangement#spacedBy
. - Pour définir l'écart entre les fiches de films, transmettez un objet
Arrangement
à la fonction composableLazyRow
avec le paramètrehorizontalArrangement
. - Pour définir un retrait au niveau de la colonne, transmettez un objet
PaddingValue
avec le paramètrecontentPadding
.
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. Implémenter l'écran d'informations
L'écran d'informations présente les détails du film sélectionné. Le fichier Details.kt
contient une fonction composable Details
. Vous allez ajouter du code à cette fonction pour implémenter l'écran d'informations.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
Afficher le titre, le nom du studio et la description du film
Un objet Movie
possède les trois attributs de chaîne de caractères suivants comme métadonnées du film :
title
: titre du filmstudio
: nom du studio qui a produit le filmdescription
: bref résumé du film
Pour afficher ces métadonnées sur l'écran d'informations, procédez comme suit :
- Ajoutez une fonction composable
Column
, puis définissez une marge verticale de 32 dp et une marge horizontale de 48 dp autour de la colonne avec l'objetModifier
créé par la méthodeModifier.padding
. - Ajoutez une fonction composable
Text
pour afficher le titre du film. - Ajoutez une fonction composable
Text
pour afficher le nom du studio. - Ajoutez une fonction composable
Text
pour afficher la description du film.
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)
}
}
L'objet Modifier
spécifié dans le paramètre de la fonction composable Details
est utilisé dans la tâche suivante.
Afficher l'image de fond associée à un objet Movie
donné
Un objet Movie
possède un attribut backgroundImageUrl
qui spécifie l'emplacement de l'image de fond du film décrit par l'objet.
Pour afficher l'image de fond d'un film donné, procédez comme suit :
- Ajoutez une fonction composable
Box
en tant que wrapper de la fonction composableColumn
avec l'objetmodifier
transmis via la fonction composableDetails
. - Dans la fonction composable
Box
, appelez la méthodefillMaxSize
de l'objetmodifier
pour que la fonction composableBox
remplisse la taille maximale pouvant être allouée à la fonction composableDetails
. - Ajoutez une fonction composable
AsyncImage
avec les paramètres suivants à la fonction composableBox
:
- Définissez la valeur de l'attribut
backgroundImageUrl
de l'objetMovie
donné sur un paramètremodel
. - Transmettez la valeur
null
à un paramètrecontentDescription
.
- Transmettez un objet
ContentScale.Crop
à un paramètrecontentScale
. Pour afficher les différentes optionsContentScale
, consultez la section Échelle de contenu. - Transmettez la valeur renvoyée par la méthode
Modifier.fillMaxSize
au paramètremodifier
.
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)
}
}
}
Utiliser l'objet MaterialTheme
pour une thématisation cohérente
L'objet MaterialTheme
contient des fonctions qui font référence à des valeurs de thème actuelles, comme celles des classes Typography
et ColorScheme
.
Pour faire référence à l'objet MaterialTheme
dans le cadre d'une thématisation cohérente, procédez comme suit :
- Définissez la propriété
MaterialTheme.typography.displayMedium
sur le style de texte du titre du film. - Définissez la propriété
MaterialTheme.typography.bodySmall
sur le style de texte des deuxièmes fonctions composablesText
. - Définissez la propriété
MaterialTheme.colorScheme.background
sur la couleur d'arrière-plan de la fonction composableColumn
avec la méthodeModifier.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)
}
}
}
Facultatif : ajuster la mise en page
Pour ajuster la mise en page de la fonction composable Details
, procédez comme suit :
- Définissez la fonction composable
Box
pour utiliser la totalité de l'espace disponible avec le modificateurfillMaxSize
. - Définissez l'arrière-plan de la fonction composable
Box
avec le modificateurbackground
pour remplir l'arrière-plan avec un dégradé linéaire créé en appelant la fonctionBrush.linearGradient
avec une liste d'objetsColor
contenant les valeursMaterialTheme.colorScheme.background
etColor.Transparent
. - Définissez la marge horizontale
48.dp
et la marge verticale24.dp
autour de la fonction composableColumn
avec le modificateurpadding
. - Définissez la largeur de la fonction composable
Column
avec le modificateurwidth
créé en appelant la fonctionModifier.width
avec la valeur0.5f
. - Ajoutez l'espace
8.dp
entre la deuxième fonction composableText
et le troisième composableText
avecSpacer
. La hauteur de la fonction composableSpacer
est spécifiée avec le modificateurheight
créé avec la fonctionModifier.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. Permettre la navigation entre les écrans
Vous disposez à présent du navigateur de catalogue et de l'écran d'informations. Lorsqu'un utilisateur sélectionne un contenu à partir du navigateur de catalogue, celui-ci doit passer à l'écran d'informations. Pour ce faire, vous utilisez le modificateur clickable
pour ajouter un écouteur event
à la fonction composable MovieCard
. Lorsque vous appuyez sur le bouton central de la croix directionnelle, la méthode CatalogBrowserViewModel#showDetails
est appelée avec l'objet Movie associé à la fonction composable MovieCard
en tant qu'argument.
- Ouvrez le fichier
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Transmettez une fonction lambda à la fonction composable
MovieCard
avec un paramètreonClick
. - Appelez le rappel
onMovieSelected
avec l'objet Movie associé à la fonction composableMovieCard
.
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. Ajouter un carrousel au navigateur de catalogue pour mettre en avant une sélection de contenus
Le carrousel est un élément d'UI couramment adapté. Il met automatiquement à jour les diapositives après une durée spécifiée. Cette fonctionnalité est généralement utilisée pour mettre en avant des sélections de contenus.
Pour ajouter un carrousel au navigateur de catalogue afin de mettre en avant une sélection de films dans la liste de contenus, procédez comme suit :
- Ouvrez le fichier
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
. - Appelez la fonction
item
pour ajouter un élément à la fonction composableLazyColumn
. - Déclarez
featuredMovieList
en tant que propriété déléguée dans le lambda transmis à la fonctionitem
. Définissez ensuite l'objetState
comme délégué ; il est collecté à partir de l'attributcatalogBrowserViewModel.featuredMovieList
. - Appelez la fonction composable
Carousel
dans la fonctionitem
, puis transmettez les paramètres suivants :
- La taille de la variable
featuredMovieList
via un paramètreslideCount
. - Un objet
Modifier
permettant de spécifier la taille du carrousel à l'aide des méthodesModifier.fillMaxWidth
etModifier.height
. La fonction composableCarousel
utilise une hauteur de 376 dp lorsqu'elle transmet une valeur376.dp
à la méthodeModifier.height
. - Un lambda appelé avec une valeur entière qui spécifie l'index de l'élément visible du carrousel.
- Récupérez l'objet
Movie
à partir de la variablefeaturedMovieList
et de la valeur d'index donnée. - Ajoutez une fonction composable
Box
à la fonction composableCarousel
. - Ajoutez une fonction composable
Text
à la fonction composableBox
pour afficher le titre du film.
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) })
}
}
}
}
}
Afficher des images de fond
La fonction composable Box
place un composant sur un autre. Pour en savoir plus, consultez Principes de base de la mise en page.
Pour afficher des images de fond, procédez comme suit :
- Appelez la fonction composable
AsyncImage
pour charger l'image de fond associée à l'objetMovie
avant la fonction composableText
. - Mettez à jour la position et le style du texte de la fonction composable
Text
pour une meilleure visibilité. - Définissez un espace réservé dans la fonction composable
AsyncImage
pour éviter un décalage de mise en page. Le code de démarrage contient un espace réservé en tant que drawable auquel vous pouvez faire référence avecR.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) })
}
}
}
}
}
Ajouter une transition vers l'écran d'informations
Vous pouvez ajouter un Button
au carrousel afin que les utilisateurs puissent déclencher une transition vers l'écran d'informations en cliquant sur le bouton.
Pour permettre aux utilisateurs d'afficher les informations du film dans le carrousel visible sur l'écran d'informations, procédez comme suit :
- Appelez la fonction composable
Column
dans le composableBox
du composableCarousel
. - Déplacez le composable
Text
dansCarousel
vers la fonction composableColumn
. - Appelez la fonction composable
Button
après la fonction composableText
dans la fonction composableColumn
. - Appelez la fonction composable
Text
dans la fonction composableButton
avec la valeur renvoyée de la fonctionstringResource
appelée avecR.string.show_details
. - Appelez la fonction
onMovieSelected
avec la variablefeaturedMovie
dans le lambda transmis au paramètreonClick
de la fonction composableButton
.
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) })
}
}
}
}
}
Facultatif : ajuster la mise en page
Pour ajuster la mise en page du carrousel, procédez comme suit :
- Attribuez la valeur
backgroundColor
avec la valeurMaterialTheme.colorScheme.background
dans la fonction composableCarousel
. - Encapsulez la fonction composable
Column
avec un composableBox
. - Transmettez la valeur
Alignment.BottomStart
au paramètrecontentAlignment
du composantBox
. - Transmettez le modificateur
fillMaxSize
au paramètre de modificateur de la fonction composableBox
. Le modificateurfillMaxSize
est créé avec la fonctionModifier.fillMaxSize()
. - Appelez la méthode
drawBehind()
sur le modificateurfillMaxSize
transmis au composableBox
. - Dans le lambda transmis au modificateur
drawBehind
, attribuez la valeurbrush
avec un objetBrush
créé en appelant la fonctionBrush.linearGradient
avec une liste de deux objetsColor
. Cette liste est créée en appelant la fonctionlistOf
avec les valeursbackgroundColor
etColor.Transparent
. - Appelez
drawRect
avec l'objetbrush
dans le lambda transmis au modificateurdrawBehind
pour créer une couche de contour sur l'image de fond. - Spécifiez la marge intérieure de la fonction composable
Column
avec le modificateurpadding
créé en appelantModifier.padding
avec la valeur20.dp
. - Ajoutez une fonction composable
Spacer
avec une valeur20.dp
entre le composableText
et le composableButton
dans la fonction composableColumn
.
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. Télécharger le code de solution
Pour télécharger le code de solution de cet atelier de programmation, effectuez l'une des opérations suivantes :
- Cliquez sur le bouton suivant pour le télécharger au format .zip, puis décompressez-le et ouvrez-le dans Android Studio.
- Récupérez-le avec Git :
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
8. Félicitations.
Félicitations ! Vous avez appris les principes de base de Compose pour la télévision :
- Comment implémenter un écran pour afficher une liste de contenu en combinant LazyColumn et LazyLow
- Comment implémenter un écran de base pour afficher les détails du contenu
- Comment ajouter des transitions lors du passage d'un écran à un autre