Présentation de Compose pour la télévision

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.

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 :

Le navigateur de catalogue affiche une sélection de films\nau-dessous d'un carrousel.\nL'écran affiche également une liste de films pour chaque catégorie.

  • 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 :

L'écran d'informations affiche les métadonnées du film,\ny compris le titre, le studio de production et une brève description.\nLes métadonnées sont affichées sur l'image de fond du film.

Ce dont vous avez besoin

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 :

$ 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 fichier solution.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

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 = {}
) {
}

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 collectAsState. Un objet Category possède un 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 :

  1. Dans Android Studio, ouvrez le fichier CatalogBrowser.kt du code de démarrage, puis ajoutez une fonction Composable TvLazyColumn à la fonction Composable CatalogBrowser.
  2. Appelez la méthode catalogBrowserViewModel.categoryList.collectAsState() pour collecter le flux en tant qu'objet State.
  3. Déclarez categoryList en tant que propriété déléguée de l'objet State créé à l'étape précédente.
  4. Appelez la fonction items avec la variable categoryList en tant que paramètre.
  5. Appelez la fonction Composable Text avec le nom de la catégorie comme paramètre transmis en tant qu'argument du 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)
        }
    }
}

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 :

  1. Ajoutez la fonction Composable TvLazyRow, puis transmettez-lui un lambda.
  2. Dans le lambda, appelez la fonction items avec category etla valeur d'attribut movieList, puis transmettez-lui un lambda.
  3. Dans le lambda transmis à la fonction items, appelez la fonction Composable MovieCard avec un objet Movie.

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)
                }
            }
        }
    }
}

Facultatif : Ajuster la mise en page

  1. Pour définir l'écart entre les catégories, transmettez un objet Arrangement à la fonction Composable TvLazyColumn avec le paramètre verticalArrangement. L'objet Arrangement est créé en appelant la méthode Arrangement#spacedBy.
  2. Pour définir l'écart entre les fiches de films, transmettez un objet Arrangement à la fonction Composable TvLazyRow avec le paramètre horizontalArrangement.
  3. Pour définir un retrait au niveau de la colonne, transmettez un objet PaddingValue avec le paramètre contentPadding.

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. 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

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) {
}

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 film
  • studio : nom du studio qui a produit le film
  • description : bref résumé du film

Pour afficher ces métadonnées sur l'écran d'informations, procédez comme suit :

  1. 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'objet Modifier créé par la méthode Modifier.padding.
  2. Ajoutez une fonction Composable Text pour afficher le titre du film.
  3. Ajoutez une fonction Composable Text pour afficher le nom du studio.
  4. Ajoutez une fonction Composable Text pour afficher la description du film.

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)
    }
}

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 :

  1. Ajoutez une fonction Composable Box en tant que wrapper de la fonction Composable Column avec l'objet modifier transmis via la fonction Composable Details.
  2. Appelez la méthode fillMaxSize de l'objet modifier dans la fonction Composable Box afin que cette dernière remplisse la taille maximale pouvant être allouée à la fonction Composable Details.
  3. Ajoutez une fonction Composable AsyncImage avec les paramètres suivants à la fonction Composable Box :
  • Définissez la valeur de l'attribut backgroundImageUrl de l'objet Movie donné sur un paramètre model.
  • Transmettez la valeur null à un paramètre contentDescription.
  • Transmettez un objet ContentScale.Crop à un paramètre contentScale. Pour afficher les différentes options ContentScale, consultez la section Échelle de contenu.
  • Transmettez la valeur renvoyée par la méthode Modifier.fillMaxSize au paramètre modifier.
  • Définissez une marge verticale de 32 dp et une marge horizontale de 48 dp autour de la colonne en définissant un objet Modifier créé en appelant la méthode Modifier.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,
            )
        }
    }
}

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][ColorScheme].

Pour faire référence à l'objet MaterialTheme dans le cadre d'une thématisation cohérente, procédez comme suit :

  1. Définissez la propriété MaterialTheme.typography.headlineLarge sur le style de texte du titre du film.
  2. Définissez la propriété MaterialTheme.typography.headlineMedium sur le style de texte des deux autres fonctions Composable Text.
  3. Définissez la propriété MaterialTheme.colorScheme.background sur la couleur d'arrière-plan de la fonction Composable Column avec la méthode Modifier.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. 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, 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.

  1. Ouvrez le fichier com.example.tvcomposeintroduction.ui.screens.CatalogBrowser.
  2. Transmettez une fonction lambda à la fonction Composable MovieCard avec un paramètre onClick.
  3. Appelez le rappel onMovieSelected avec l'objet Movie associé à la fonction Composable 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. 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 :

  1. Ouvrez le fichier com.example.tvcomposeintroduction.ui.screens.CatalogBrowser.
  2. Appelez la fonction item pour ajouter un élément à la fonction Composable TvLazyColumn.
  3. Déclarez featuredMovieList en tant que propriété déléguée dans le lambda transmis à la fonction item. Définissez ensuite l'objet State comme délégué ; il est collecté à partir de l'attribut catalogBrowserViewModel.featuredMovieList.
  4. Appelez la fonction Composable Carousel dans la fonction item, puis transmettez les paramètres suivants :
  • La taille de la variable featuredMovieList via un paramètre slideCount
  • Un objet Modifier permettant de spécifier la taille du carrousel à l'aide des méthodes Modifier.fillMaxWidth et Modifier.height. La fonction Composable Carousel utilise une hauteur de 376 dp lorsqu'elle transmet une valeur 376.dp à la méthode Modifier.height
  • Un Lambda appelé avec une valeur entière qui spécifie l'index de l'élément visible du carrousel
  1. Récupérez l'objet Movie à partir de la variable featuredMovieList et de la valeur d'index donnée.
  2. Ajoutez une fonction Composable CarouselSlide à la fonction Composable Carousel.
  3. Ajoutez une fonction Composable Text à la fonction CarouselSlide Composable pour afficher le titre du film.

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) })
                }
            }
        }
    }
}

Afficher des images de fond

La fonction Composable CarouselSlide peut utiliser un autre lambda pour spécifier l'affichage de son arrière-plan.

Pour afficher des images de fond, procédez comme suit :

  1. Transmettez un lambda à la fonction Composable CarouselSlide avec le paramètre background.
  2. Appelez la fonction Composable AsyncImage pour charger l'image de fond associée à l'objet Movie en arrière-plan de la fonction Composable CarouselSlide.
  3. Modifiez la position et le style du texte de la fonction Composable Text dans la fonction Composable CarouselSlide pour une meilleure visibilité.
  4. 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 avec R.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) })
                }
            }
        }
    }
}

Ajouter une transition vers l'écran d'informations

Vous pouvez permettre aux utilisateurs de cliquer sur la fonction Composable CarouselSlide.

Pour permettre aux utilisateurs d'afficher les informations du film dans le carrousel visible sur l'écran d'informations, procédez comme suit :

  1. Transmettez la valeur renvoyée par la méthode Modifier.clickable à la fonction Composable CarouselSlide via le paramètre modifier.
  2. Appelez la fonction onMovieSelected avec l'objet Movie pour la fonction Composable CarouselSlide visible dans le lambda transmis à la méthode Modifier.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. 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 TvLazyColumn et TvLazyLow
  • Comment implémenter un écran de base pour afficher les détails du contenu
  • Comment ajouter des transitions lors du passage d'un écran à une autre