Ajouter une liste déroulante

1. Avant de commencer

Dans cet atelier de programmation, vous apprendrez à créer une liste déroulante dans votre application avec Jetpack Compose.

Vous utiliserez l'application Affirmations qui affiche une liste d'affirmations associées à de superbes images pour faire souffler un vent d'optimisme sur votre journée.

Les données existent déjà. Il vous suffit de les récupérer et de les afficher dans l'interface utilisateur.

Conditions préalables

  • Vous maîtrisez les listes en Kotlin.
  • Vous savez comment créer des mises en page avec Jetpack Compose.
  • Vous savez comment exécuter des applications sur un appareil ou un émulateur.

Points abordés

  • Créer une fiche Material Design à l'aide de Jetpack Compose.
  • Créer une liste déroulante à l'aide de Jetpack Compose.

Objectifs de l'atelier

  • Vous utiliserez une application existante et ajouterez une liste déroulante à l'interface utilisateur.

Le résultat final se présentera comme suit :

286f5132aa155fa6.png

Ce dont vous avez besoin

  • Un ordinateur avec accès à Internet, un navigateur Web et Android Studio
  • Un accès à GitHub

Télécharger le code de démarrage

Dans Android Studio, ouvrez le dossier basic-android-kotlin-compose-training-affirmations.

L'application doit afficher un écran vide lorsqu'elle est compilée à partir du code guichet starter.

3beea0789e2eeaba.png

2. Créer une classe de données d'élément de liste

Créer une classe de données pour une affirmation

Dans les applications Android, les listes sont composées d'éléments. Pour les données individuelles, il peut s'agir d'éléments simples comme une chaîne ou un entier. Pour les éléments de liste qui contiennent divers types de données, comme une image et du texte, vous aurez besoin d'une classe contenant l'ensemble de ces propriétés. Une classe de données est un type qui ne contient que des propriétés. Elle peut fournir des méthodes utilitaires qui fonctionnent avec ces propriétés.

  1. Créez un package sous com.example.affirmations.

89c8d8485c685fac.png

Nommez le nouveau package model. Le package "model" contient le modèle de données qui sera représenté par une classe de données. La classe de données comprend des propriétés qui représentent les informations correspondant à ce qui sera une "affirmation", à savoir une ressource de chaîne et une ressource d'image. Les packages sont des répertoires qui contiennent des classes et même d'autres répertoires.

b54fb6bf57de44c8.png

  1. Créez une classe dans le package com.example.affirmations.model.

58510a651bd49100.png

Nommez la nouvelle classe Affirmation et définissez-la en tant que Classe de données.

7f94b65ee3d8407f.png

  1. Chaque Affirmation se compose d'une image et d'une chaîne. Créez deux propriétés val dans la classe de données Affirmation. L'une doit être nommée stringResourceId, et l'autre imageResourceId. Dans les deux cas, il doit s'agir d'entiers.

Affirmation.kt

data class Affirmation(
    val stringResourceId: Int,
    val imageResourceId: Int
)
  1. Annotez la propriété stringResourceId avec l'annotation @StringRes et annotez imageResourceId avec l'annotation @DrawableRes. stringResourceId représente un ID du texte d'affirmation stocké dans une ressource de chaîne. imageResourceId représente un ID de l'image d'affirmation stockée dans une ressource drawable.

Affirmation.kt

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes

data class Affirmation(
    @StringRes val stringResourceId: Int,
    @DrawableRes val imageResourceId: Int
)
  1. Dans le package com.example.affirmations.data, ouvrez le fichier Datasource.kt et annulez la mise en commentaire des deux instructions d'importation et du contenu de la classe Datasource.

Datasource.kt

import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

class Datasource() {
    fun loadAffirmations(): List<Affirmation> {
        return listOf<Affirmation>(
            Affirmation(R.string.affirmation1, R.drawable.image1),
            Affirmation(R.string.affirmation2, R.drawable.image2),
            Affirmation(R.string.affirmation3, R.drawable.image3),
            Affirmation(R.string.affirmation4, R.drawable.image4),
            Affirmation(R.string.affirmation5, R.drawable.image5),
            Affirmation(R.string.affirmation6, R.drawable.image6),
            Affirmation(R.string.affirmation7, R.drawable.image7),
            Affirmation(R.string.affirmation8, R.drawable.image8),
            Affirmation(R.string.affirmation9, R.drawable.image9),
            Affirmation(R.string.affirmation10, R.drawable.image10))
    }
}

3. Ajouter une liste à votre application

Créer une vignette d'élément de liste

Cette application est destinée à afficher une liste d'affirmations. Lorsque vous configurez l'UI pour afficher une liste, la première étape consiste à créer un élément de liste. Chaque élément d'affirmation se compose d'une image et d'une chaîne. Les données de chacun de ces éléments sont fournies avec le code de démarrage. Vous créerez le composant d'interface utilisateur pour afficher ces éléments.

L'élément sera constitué d'un composable Card contenant les composables Image et Text. Dans Compose, un élément Card est une surface qui affiche du contenu et des actions dans un même conteneur. La vignette Affirmation se présentera comme suit dans l'aperçu :

4f657540712a069f.png

La vignette affiche une image avec du texte en dessous. Cette mise en page verticale peut être obtenue à l'aide d'un composable Column encapsulé dans un composable Card. Pour parvenir à ce résultat, vous pouvez essayer par vous-même ou suivre les étapes ci-dessous.

  1. Ouvrez le fichier MainActivity.kt.
  2. Sous AffirmationsApp(), créez une méthode appelée AffirmationCard() et ajoutez-lui l'annotation @Composable.

MainActivity.kt

@Composable
fun AffirmationsApp() {
}

@Composable
fun AffirmationCard() {

}
  1. Modifiez la signature de la méthode pour utiliser un objet Affirmation comme paramètre. L'objet Affirmation provient du package model.

MainActivity.kt

import com.example.affirmations.model.Affirmation

@Composable
fun AffirmationCard(affirmation: Affirmation) {

}
  1. Ajoutez un paramètre modifier à la signature. Définissez la valeur par défaut Modifier pour le paramètre.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {

}
  1. Dans la méthode AffirmationCard, appelez le composable Card. Transmettez le paramètre modifier.

MainActivity.kt

import androidx.compose.material3.Card

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {

    }
}
  1. Ajoutez un composable Column dans le composable Card. Les éléments d'un composable Column sont organisés verticalement dans l'UI. Cela vous permet de placer une image au-dessus du texte associé. À l'inverse, un composable Row organise les éléments qu'il contient horizontalement.

MainActivity.kt

import androidx.compose.foundation.layout.Column

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {

        }
    }

}
  1. Ajoutez un composable Image dans le corps lambda du composable Column. Rappelez-vous qu'un composable Image doit toujours avoir une ressource à afficher, ainsi qu'un contentDescription. La ressource doit être un élément painterResource transmis au paramètre painter. La méthode painterResource charge soit des drawables vectoriels, soit des formats d'élément rastérisés comme des fichiers PNG. Transmettez également un élément stringResource pour le paramètre contentDescription.

MainActivity.kt

import androidx.compose.foundation.Image
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
            )
        }
    }
}
  1. En plus des paramètres painter et contentDescription, transmettez les éléments modifier et contentScale. contentScale détermine la manière dont l'image est mise à l'échelle et affichée. L'attribut fillMaxWidth de l'objet Modifier doit être défini et sa hauteur doit être de 194.dp. contentScale doit être défini sur ContentScale.Crop.

MainActivity.kt

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.unit.dp
import androidx.compose.ui.layout.ContentScale

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
        }
    }
}
  1. Dans Column, créez un composable Text après le composable Image. Transmettez un élément stringResource correspondant à affirmation.stringResourceId au paramètre text, transmettez un objet Modifier avec l'attribut padding défini sur 16.dp et définissez un thème de texte en transmettant MaterialTheme.typography.headlineSmall au paramètre style.

MainActivity.kt

import androidx.compose.material3.Text
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.platform.LocalContext

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
            Text(
                text = LocalContext.current.getString(affirmation.stringResourceId),
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.headlineSmall
            )
        }
    }
}

Prévisualiser le composable AffirmationCard

La vignette est au cœur de l'interface utilisateur de l'application Affirmations, et vous avez travaillé dur pour la créer. Pour vous assurer que la vignette s'affiche correctement, vous pouvez créer un composable qui peut être prévisualisé sans lancer l'intégralité de l'application.

  1. Créez une méthode privée appelée AffirmationCardPreview(). Annotez la méthode avec @Preview et @Composable.

MainActivity.kt

import androidx.compose.ui.tooling.preview.Preview

@Preview
@Composable
private fun AffirmationCardPreview() {

}
  1. Dans cette méthode, appelez le composable AffirmationCard et transmettez-lui un nouvel objet Affirmation avec la ressource de chaîne R.string.affirmation1 et la ressource drawable R.drawable.image1 transmise à son constructeur.

MainActivity.kt

@Preview
@Composable
private fun AffirmationCardPreview() {
    AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
  1. Ouvrez l'onglet Split (Diviser) pour afficher un aperçu de AffirmationCard. Si nécessaire, cliquez sur Build & Refresh (Compiler et actualiser) dans le volet Design (Conception) pour afficher l'aperçu.

924a4df2c1db236c.png

Créer la liste

Le composant d'élément de liste est le composant principal d'une liste. Une fois l'élément de liste créé, vous pouvez l'exploiter pour créer le composant de liste proprement dit.

  1. Créez une fonction appelée AffirmationList(), ajoutez-lui l'annotation @Composable et déclarez une liste (List) d'objets Affirmation en tant que paramètre dans la signature de la méthode.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>) {

}
  1. Déclarez un objet modifier en tant que paramètre dans la signature de la méthode avec la valeur par défaut Modifier.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {

}
  1. Dans Jetpack Compose, une liste déroulante peut être créée à l'aide du composable LazyColumn. Un élément LazyColumn se différencie d'un élément Column en ce sens que Column doit être utilisé lorsqu'il y a peu d'éléments à afficher, car Compose les charge tous en même temps. Un élément Column ne peut contenir qu'un nombre prédéfini, ou fixe, de composables. Un élément LazyColumn peut ajouter du contenu à la demande. Cela convient pour les longues listes, en particulier lorsque la longueur de la liste est inconnue. Un élément LazyColumn accepte également le défilement par défaut, sans code supplémentaire. Déclarez un composable LazyColumn dans la fonction AffirmationList(). Transmettez l'objet modifier en tant qu'argument à LazyColumn.

MainActivity.kt

import androidx.compose.foundation.lazy.LazyColumn

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {

    }
}
  1. Dans le corps lambda de LazyColumn, appelez la méthode items() et transmettez affirmationList. La méthode items() permet d'ajouter des éléments à LazyColumn. Cette méthode est propre à ce composable. Il ne s'agit pas d'une pratique courante pour la plupart des composables.

MainActivity.kt

import androidx.compose.foundation.lazy.items

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) {

        }
    }
}
  1. Une fonction lambda est requise pour appeler la méthode items(). Dans cette fonction, spécifiez un paramètre affirmation représentant un élément d'affirmation provenant de la propriété affirmationList.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->

        }
    }
}
  1. Pour chaque affirmation de la liste, appelez le composable AffirmationCard(). Transmettez-lui l'objet affirmation et un objet Modifier avec l'attribut padding défini sur 8.dp.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->
            AffirmationCard(
                affirmation = affirmation,
                modifier = Modifier.padding(8.dp)
            )
        }
    }
}

Afficher la liste

  1. Dans le composable AffirmationsApp, récupérez les orientations de mise en page actuelles et enregistrez-les dans une variable. Elles seront utilisées pour configurer la marge intérieure ultérieurement.

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
}
  1. À présent, créez un composable Surface. Ce composable définira la marge intérieure du composable AffirmationsList.

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface() {
    }
}
  1. Transmettez un Modifier au composable Surface qui remplit la largeur et la hauteur maximales de son parent, définit la marge intérieure de la barre d'état et définit les marges intérieures de début et de fin sur layoutDirection. Voici un exemple de conversion d'un objet LayoutDirection en marge intérieure : WindowInsets.safeDrawing.asPaddingValues().calculateStartPadding(layoutDirection).

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
    }
}
  1. Dans le lambda du composable Surface, appelez le composable AffirmationList et transmettez DataSource().loadAffirmations() au paramètre affirmationList.

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
        AffirmationsList(
            affirmationList = Datasource().loadAffirmations(),
        )
    }
}

Exécutez l'application Affirmations sur un appareil ou un émulateur pour voir le résultat final.

286f5132aa155fa6.png

4. Télécharger le code de solution

Pour télécharger le code de cet atelier de programmation terminé, utilisez les commandes Git suivantes :

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-affirmations.git
$ cd basic-android-kotlin-compose-training-affirmations
$ git checkout intermediate

Vous pouvez également télécharger le dépôt sous forme de fichier ZIP, le décompresser et l'ouvrir dans Android Studio.

Si vous souhaitez voir le code de solution, affichez-le sur GitHub.

5. Conclusion

Vous savez maintenant comment créer des fiches, des éléments de liste et des listes déroulantes à l'aide de Jetpack Compose. Gardez à l'esprit qu'il ne s'agit que d'outils de base pour créer une liste. Vous pouvez laisser libre cours à votre créativité et personnaliser les éléments de la liste comme vous le souhaitez.

Résumé

  • Utilisez des composables Card pour créer des éléments de liste.
  • Modifiez l'UI contenue dans un composable Card.
  • Créez une liste déroulante à l'aide du composable LazyColumn.
  • Créez une liste à l'aide d'éléments de liste personnalisés.