Ajouter Compose à une application basée sur les vues

1. Avant de commencer

Dès le départ, Jetpack Compose a été conçu avec l'interopérabilité des vues. En d'autres termes, Compose et le système de vues peuvent partager des ressources et travailler ensemble pour afficher l'UI. Cette fonctionnalité vous permet d'ajouter Compose à une application existante basée sur les vues. Compose et les vues peuvent ainsi coexister dans votre codebase jusqu'à ce que votre application soit entièrement dans Compose.

Dans cet atelier de programmation, vous allez remplacer l'élément de liste basé sur les vues de l'application Juice Tracker par Compose. Si vous le souhaitez, vous pouvez convertir vous-même les autres vues de l'application Juice Tracker.

Si l'UI de votre application est basée sur les vues, vous préférerez peut-être la réécrire par étape plutôt qu'en une seule fois. Cet atelier de programmation vous aidera à convertir une vue unique d'une UI basée sur des vues en élément Compose.

Conditions préalables

  • Vous devez maîtriser le concept d'UI basée sur les vues.
  • Vous devez savoir créer une application à l'aide d'une UI basée sur les vues.
  • Vous devez connaître la syntaxe du langage Kotlin, y compris les lambdas.
  • Vous devez savoir créer une application dans Jetpack Compose.

Points abordés

  • Ajouter Compose à un écran existant créé avec des vues Android
  • Prévisualiser un composable ajouté à une application basée sur les vues

Objectifs de l'atelier

  • Convertir un élément de liste basé sur une vue en élément Compose dans l'application Juice Tracker

2. Présentation de l'application de démarrage

Cet atelier de programmation utilise le code de solution de l'application Juice Tracker issue de la page Créer une application Android avec des vues comme code de démarrage. L'application de démarrage enregistre déjà les données à l'aide de la bibliothèque de persistance de Room. L'utilisateur peut ajouter des informations sur le jus de fruits à la base de données de l'application, comme son nom, sa description, sa couleur et sa note.

36bd5542e97fee2e.png

Dans cet atelier de programmation, vous allez convertir l'élément de liste basé sur une vue en élément Compose.

Élément de liste avec des informations sur un jus

Télécharger le code de démarrage pour cet atelier de programmation

Pour commencer, téléchargez le code de démarrage :

Vous pouvez également cloner le dépôt GitHub du code :

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git
$ cd basic-android-kotlin-compose-training-juice-tracker
$ git checkout views

Vous pouvez parcourir le code dans le dépôt GitHub JuiceTracker.

3. Ajouter une bibliothèque Jetpack Compose

Compose et les vues peuvent coexister sur un même écran. En effet, vous pouvez avoir des éléments d'interface utilisateur dans Compose et d'autres dans le système de vues. Par exemple, vous pouvez choisir de n'utiliser que la liste dans Compose et de laisser le reste de l'écran dans le système de vues.

Suivez les étapes ci-dessous pour ajouter la bibliothèque Compose à l'application Juice Tracker.

  1. Ouvrez Juice Tracker dans Android Studio.
  2. Ouvrez le fichier build.gradle.kts au niveau de l'application.
  3. Dans le bloc buildFeatures, ajoutez un indicateur compose = true.
buildFeatures {
    //...
    // Enable Jetpack Compose for this module
    compose = true
}

Cet indicateur permet à Android Studio d'interagir avec Compose. Vous n'avez pas effectué cette étape dans les précédents ateliers de programmation, car Android Studio génère automatiquement ce code lorsque vous créez un projet de modèle Compose Android Studio.

  1. Sous buildFeatures, ajoutez le bloc composeOptions.
  2. Dans le bloc, définissez kotlinCompilerExtensionVersion sur "1.5.1" pour définir la version du compilateur Kotlin.
composeOptions {
    kotlinCompilerExtensionVersion = "1.5.1"
}
  1. Dans la section dependencies, ajoutez des dépendances Compose. Vous devez disposer des dépendances suivantes pour ajouter Compose à une application basée sur des vues. Elles permettent d'intégrer Compose à l'activité, d'ajouter la bibliothèque de composants de conception Compose, d'accepter la thématisation Jetpack correspondante et de fournir des outils permettant une meilleure compatibilité avec l'IDE.
dependencies {
    implementation(platform("androidx.compose:compose-bom:2023.06.01"))
    // other dependencies
    // Compose
    implementation("androidx.activity:activity-compose:1.7.2")
    implementation("androidx.compose.material3:material3")
    implementation("com.google.accompanist:accompanist-themeadapter-material3:0.28.0")

    debugImplementation("androidx.compose.ui:ui-tooling")
}

Ajouter ComposeView

Un ComposeView est une vue Android pouvant héberger le contenu de l'UI de Jetpack Compose. Utilisez setContent afin de fournir la fonction composable du contenu pour la vue.

  1. Ouvrez layout/list_item.xml et affichez l'aperçu dans l'onglet Split (Diviser).

À la fin de cet atelier de programmation, vous remplacerez cette vue par un composable.

7a2df616fde1ec56.png

  1. Dans JuiceListAdapter.kt, supprimez ListItemBinding de l'ensemble du code. Dans la classe JuiceListViewHolder, remplacez binding.root par composeView.
import androidx.compose.ui.platform.ComposeView

class JuiceListViewHolder(
    private val onEdit: (Juice) -> Unit,
    private val onDelete: (Juice) -> Unit
): RecyclerView.ViewHolder(composeView)
  1. Dans le dossier onCreateViewHolder(), mettez à jour la fonction return() pour qu'elle corresponde au code suivant :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JuiceListViewHolder {
   return JuiceListViewHolder(
       ComposeView(parent.context),
       onEdit,
       onDelete
   )
}
  1. Dans la classe JuiceListViewHolder, supprimez toutes les variables private et l'intégralité du code de la fonction bind(). La classe JuiceListViewHolder ressemble maintenant au code suivant :
class JuiceListViewHolder(
    private val onEdit: (Juice) -> Unit,
    private val onDelete: (Juice) -> Unit
) : RecyclerView.ViewHolder(composeView) {

   fun bind(juice: Juice) {

   }
}
  1. À ce stade, vous pouvez supprimer les importations com.example.juicetracker.databinding.ListItemBinding et android.view.LayoutInflater.
// Delete
import com.example.juicetracker.databinding.ListItemBinding
import android.view.LayoutInflater
  1. Supprimez le fichier layout/list_item.xml.
  2. Sélectionnez OK dans la boîte de dialogue Delete (Supprimer).

2954ed44c5827571.png

4. Ajouter une fonction composable

Ensuite, créez un composable qui émet l'élément de liste. Le composable nécessite Juice et deux fonctions de rappel pour modifier et supprimer l'élément de liste.

  1. Dans JuiceListAdapter.kt, après la définition de la classe JuiceListAdapter, créez une fonction composable nommée ListItem().
  2. Faites en sorte que la fonction ListItem() accepte l'objet Juice et un rappel lambda pour la suppression.
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun ListItem(
    input: Juice,
    onDelete: (Juice) -> Unit,
    modifier: Modifier = Modifier
) {
}

Examinez l'aperçu de l'élément de liste que vous souhaitez créer. Une icône de jus de fruits, des informations le concernant et un bouton de suppression sont également visibles. Vous allez bientôt implémenter ces composants.

4ec7f82371c6bc15.png

Créer un composable pour l'icône de jus de fruits

  1. Dans JuiceListAdapter.kt, après le composable ListItem(), créez une autre fonction composable nommée JuiceIcon(), qui accepte un color et un Modifier.
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {

}
  1. Dans la fonction JuiceIcon(), ajoutez des variables pour color et la description du contenu, comme indiqué dans le code suivant :
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
   val colorLabelMap = JuiceColor.values().associateBy { stringResource(it.label) }
   val selectedColor = colorLabelMap[color]?.let { Color(it.color) }
   val juiceIconContentDescription = stringResource(R.string.juice_color, color)

}

Vous allez récupérer la ressource de couleur associée à la sélection de l'utilisateur à l'aide des variables colorLabelMap et selectedColor.

  1. Ajoutez une mise en page Box pour que les deux icônes ic_juice_color et ic_juice_clear se superposent. L'icône ic_juice_color a une teinte et est centrée.
import androidx.compose.foundation.layout.Box

Box(
   modifier.semantics {
       contentDescription = juiceIconContentDescription
   }
) {
   Icon(
       painter = painterResource(R.drawable.ic_juice_color),
       contentDescription = null,
       tint = selectedColor ?: Color.Red,
       modifier = Modifier.align(Alignment.Center)
   )
   Icon(painter = painterResource(R.drawable.ic_juice_clear), contentDescription = null)
}

Comme vous connaissez bien l'implémentation de ce composable, vous ne trouverez pas ici de détails spécifiques sur la marche à suivre pour l'implémenter.

  1. Ajoutez une fonction pour afficher un aperçu de JuiceIcon(). Transmettez la couleur en tant que Yellow.
import androidx.compose.ui.tooling.preview.Preview

@Preview
@Composable
fun PreviewJuiceIcon() {
    JuiceIcon("Yellow")
}

c016198f82a5d199.png

Créer des composables de détails sur les jus de fruits

Dans JuiceListAdapter.kt, vous devez ajouter une autre fonction modulable pour afficher les détails du jus de fruits. Vous avez également besoin d'une mise en page en colonnes pour afficher deux composables Text pour le nom et la description, ainsi qu'un indicateur d'évaluation. Pour ce faire, procédez comme suit :

  1. Ajoutez une fonction composable appelée JuiceDetails() qui reçoit un objet Juice et un Modifier, ainsi qu'un composable de texte pour le nom du jus et un composable pour sa description, comme illustré dans le code suivant :
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight

@Composable
fun JuiceDetails(juice: Juice, modifier: Modifier = Modifier) {
   Column(modifier, verticalArrangement = Arrangement.Top) {
       Text(
           text = juice.name,
           style = MaterialTheme.typography.h5.copy(fontWeight = FontWeight.Bold),
       )
       Text(juice.description)
       RatingDisplay(rating = juice.rating, modifier = Modifier.padding(top = 8.dp))
   }
}
  1. Pour résoudre l'erreur de référence non résolue, créez une fonction composable nommée RatingDisplay().

536030e2ecb01a4e.png

Le système de vues comprend un RatingBar pour afficher la barre d'évaluation suivante. Compose n'a pas de composable pour la barre d'évaluation. Vous devez donc implémenter cet élément à partir de zéro.

  1. Définissez la fonction RatingDisplay() pour afficher les étoiles conformément à la note. Cette fonction composable affiche le nombre d'étoiles en fonction de la note.

Barre d'évaluation avec quatre étoiles

import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource

@Composable
fun RatingDisplay(rating: Int, modifier: Modifier = Modifier) {
   val displayDescription = pluralStringResource(R.plurals.number_of_stars, count = rating)
   Row(
       // Content description is added here to support accessibility
       modifier.semantics {
           contentDescription = displayDescription
       }
   ) {
       repeat(rating) {
           // Star [contentDescription] is null as the image is for illustrative purpose
           Image(
               modifier = Modifier.size(32.dp),
               painter = painterResource(R.drawable.star),
               contentDescription = null
           )
       }
   }
}

Pour créer le drawable "étoile" dans Compose, vous devez créer l'élément de vecteur d'étoile.

  1. Dans le volet Project (Projet), effectuez un clic droit sur drawable > New > Vector Asset (drawable > Nouveau > Élément vectoriel).

201431ca3d212113.png

  1. Dans la boîte de dialogue Asset Studio, recherchez une icône en forme d'étoile. Sélectionnez l'icône d'étoile qui est remplie.

9956ed24371f61ac.png

5a79bac6f3982b72.png

  1. Remplacez la valeur de la couleur de l'étoile par 625B71.

44d4bdfa93bc369a.png

  1. Cliquez sur Next > Finish (Suivant > Terminer).
  2. Notez qu'un drawable apparaît dans le dossier res/drawable.

64bb8d9f05019229.png

  1. Ajoutez un composable d'aperçu pour prévisualiser le composable JuiceDetails.
@Preview
@Composable
fun PreviewJuiceDetails() {
    JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}

avec la description du jus de fruits et la barre d'évaluation

Créer un composable pour le bouton de suppression

  1. Dans JuiceListAdapter.kt, ajoutez une autre fonction composable appelée DeleteButton() qui utilise une fonction de rappel de lambdas et un modificateur.
  2. Définissez le lambda sur l'argument onClick et transmettez l'Icon(), comme indiqué dans le code suivant :
import androidx.compose.ui.res.painterResource
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton

@Composable
fun DeleteButton(onDelete: () -> Unit, modifier: Modifier = Modifier) {
    IconButton(
        onClick = { onDelete() },
        modifier = modifier
    ) {
        Icon(
            painter = painterResource(R.drawable.ic_delete),
            contentDescription = stringResource(R.string.delete)
        )
    }
}
  1. Ajoutez une fonction d'aperçu pour prévisualiser le bouton de suppression.
@Preview
@Composable
fun PreviewDeleteIcon() {
    DeleteButton({})
}

Aperçu de l'icône de suppression dans Android Studio

5. Implémenter la fonction ListItem

Maintenant que vous disposez de tous les composables requis pour afficher l'élément de liste, vous pouvez les organiser dans une mise en page. Notez la fonction ListItem() que vous avez définie à l'étape précédente.

@Composable
fun ListItem(
   input: Juice,
   onEdit: (Juice) -> Unit,
   onDelete: (Juice) -> Unit,
   modifier: Modifier = Modifier
) {
}

Dans JuiceListAdapter.kt, procédez comme suit pour implémenter la fonction ListItem() :

  1. Ajoutez une mise en page Row dans le lambda Mdc3Theme {}.
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import com.google.accompanist.themeadapter.material3.Mdc3Theme

Mdc3Theme {
   Row(
       modifier = modifier,
       horizontalArrangement = Arrangement.SpaceBetween
   ) {

   }
}
  1. Dans le lambda Row, appelez les trois composables JuiceIcon, JuiceDetails et DeleteButton que vous avez créés en tant qu'éléments enfants.
JuiceIcon(input.color)
JuiceDetails(input, Modifier.weight(1f))
DeleteButton({})

La transmission du Modifier.weight(1f) au composable JuiceDetails() garantit que les détails du jus de fruits occupent l'espace horizontal restant après la mesure des éléments enfants non pondérés.

  1. Transmettez le lambda et le modificateur onDelete(input) avec l'alignement en haut en tant que paramètres du composable DeleteButton.
DeleteButton(
   onDelete = {
       onDelete(input)
   },
   modifier = Modifier.align(Alignment.Top)
)
  1. Écrivez une fonction d'aperçu pour prévisualiser le composable ListItem.
@Preview
@Composable
fun PreviewListItem() {
   ListItem(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4), {})
}

Aperçu d'Android Studio avec des détails sur le jus de betterave

  1. Liez le composable ListItem au conteneur de la vue. Appelez onEdit(input) dans la fonction lambda clickable() pour ouvrir la boîte de dialogue de modification lorsque l'utilisateur clique sur l'élément de liste.

Dans la classe JuiceListViewHolder et dans la fonction bind(), vous devez héberger le composable. Utilisez ComposeView, qui est une vue Android pouvant héberger le contenu de l'UI Compose à l'aide de sa méthode setContent.

fun bind(input: Juice) {
    composeView.setContent {
        ListItem(
            input,
            onDelete,
            modifier = Modifier
                .fillMaxWidth()
                .clickable {
                    onEdit(input)
                }
                .padding(vertical = 8.dp, horizontal = 16.dp),
       )
   }
}
  1. Exécutez l'application, puis ajoutez votre jus préféré. Notez l'élément de liste Compose qui brille.

aadccf32ab952d0f.png. 8aa751f4cf63bf98.png

Félicitations ! Vous venez de créer votre première application d'interopérabilité Compose, qui utilise des éléments Compose dans une application basée sur les vues.

6. 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-juice-tracker.git
$ cd basic-android-kotlin-compose-training-juice-tracker
$ git checkout views-with-compose

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.

7. En savoir plus

Documentation pour les développeurs Android

Atelier de programmation [intermédiaire]