Créer une application Android avec des vues

1. Avant de commencer

Introduction

Vous savez maintenant tout ce qu'il y a à connaître sur la création d'applications Android avec Compose. C'est un réel atout ! Compose est un outil très puissant qui contribue à simplifier le processus de développement. Cependant, les applications Android n'ont pas toujours été conçues avec des interfaces utilisateur déclaratives. Compose est un outil très récent dans l'histoire des applications Android. À l'origine, les interfaces utilisateur Android étaient conçues avec des vues. Par conséquent, il est fort probable que vous rencontriez des vues à mesure que vous progresserez sur votre parcours en tant que développeur Android. Dans cet atelier de programmation, vous allez découvrir les bases de la création d'applications Android avant Compose. Vous verrez donc comment utiliser le format XML, les vues, les liaisons de vue et les fragments.

Conditions préalables :

  • Vous avez suivi le cours sur les principes de base d'Android dans Compose jusqu'au module 7.

Ce dont vous avez besoin

  • Un ordinateur avec un accès à Internet et Android Studio
  • Un appareil ou un émulateur
  • Le code de démarrage de l'application Juice Tracker

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez utiliser l'application Juice Tracker. Celle-ci vous permet d'effectuer le suivi de jus de fruits phares en créant une liste d'éléments détaillés. Vous ajouterez et modifierez des fragments et du code XML pour compléter l'UI et le code de démarrage. Plus précisément, vous créerez le formulaire d'ajout d'un nouveau jus de fruits, y compris l'UI et toute logique ou navigation associée. Vous obtiendrez alors une application avec une liste vide à laquelle vous pourrez ajouter vos propres jus.

d6dc43171ae62047.png 87b2ca7b49e814cb.png 2d630489477e216e.png

2. Télécharger le code de démarrage

  1. Dans Android Studio, ouvrez le dossier basic-android-kotlin-compose-training-juice-tracker.
  2. Ouvrez le code de l'application Juice Tracker dans Android Studio.

3. Créer une mise en page

Lorsque vous créez une application avec des Views, vous construisez l'UI dans une mise en page. Les mises en page sont généralement déclarées au format XML. Ces fichiers XML de mise en page se trouvent dans le répertoire de ressources sous res > layout. Les mises en page contiennent les composants de l'UI. Ces composants sont appelés des Views. La syntaxe XML se constitue de balises, d'éléments et d'attributs. Pour en savoir plus sur la syntaxe XML, consultez l'atelier de programmation Créer des mises en page XML pour Android.

Dans cette section, vous allez créer une mise en page XML pour la boîte de dialogue de saisie Type of juice (Type de jus) illustrée sur cette image.

87b2ca7b49e814cb.png

  1. Créez un fichier de ressources de mise en page dans le répertoire main > res > layout. Appelez-le fragment_entry_dialog.

Volet Project (Projet) Android Studio ouvert avec une option permettant de créer un fichier de ressources de mise en page.

6adb279d6e74ab13.png

La mise en page fragment_entry_dialog.xml contient les composants d'UI que l'application présente à l'utilisateur.

Notez que l'élément racine est ConstraintLayout. Ce type de mise en page est un ViewGroup qui vous permet de positionner et de dimensionner des vues de manière flexible en tenant compte des contraintes. Un ViewGroup est un type de View contenant d'autres Views, appelées enfants ou Views enfants. Les étapes suivantes couvrent ce sujet plus en détail, mais vous pouvez vous familiariser avec ConstraintLayout dans la section Créer une UI responsive avec ConstraintLayout.

  1. Après avoir créé le fichier, définissez l'espace de noms de l'application dans le ConstraintLayout.

fragment_entry_dialog.xml

<androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
  1. Ajoutez les consignes suivantes au ConstraintLayout.

fragment_entry_dialog.xml

<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_left"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_middle"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/guideline_top"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="horizontal"
   app:layout_constraintGuide_begin="16dp" />

Ces Guidelines servent de remplissage pour les autres vues. Les directives contraignent le texte d'en-tête Type of juice (Type de jus).

  1. Créez un élément TextView. Cet élément TextView représente le titre du fragment de détail.

110cad4ae809e600.png

  1. Définissez TextView comme un id deheader_title.
  2. Définissez layout_width sur 0dp. Au final, les contraintes de mise en page définiront la largeur de cet objet TextView. Par conséquent, spécifier une largeur n'ajoute que des calculs inutiles pendant le dessin de l'interface utilisateur. Définir une largeur de 0dp évite les calculs supplémentaires.
  3. Définissez l'attribut TextView text sur @string/juice_type.
  4. Définissez textAppearance sur @style/TextAppearance.MaterialComponents.Headline5.

fragment_entry_dialog.xml

<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5" />

Enfin, définissez les contraintes. Contrairement aux Guideline, qui utilisent des dimensions comme contraintes, les directives elles-mêmes contraignent cet objet TextView. Pour ce faire, vous pouvez référencer l'ID de la Guideline qui doit contraindre la vue.

  1. Contraignez la partie supérieure de l'en-tête en bas de guideline_top.

fragment_entry_dialog.xml

<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
   app:layout_constraintTop_toBottomOf="@+id/guideline_top" />
  1. Contraignez la fin au début de guideline_middle et le début au début de guideline_left pour terminer le positionnement de TextView. Gardez à l'esprit que la manière dont vous contraignez une vue dépend entièrement de l'apparence de votre interface utilisateur.

fragment_entry_dialog.xml

<TextView
   android:id="@+id/header_title"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   android:text="@string/juice_type"
   android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
   app:layout_constraintTop_toBottomOf="@+id/guideline_top"
   app:layout_constraintEnd_toStartOf="@+id/guideline_middle"
   app:layout_constraintStart_toStartOf="@+id/guideline_left" />

Essayez de créer le reste de l'interface utilisateur à partir des captures d'écran. Vous trouverez le fichier fragment_entry_dialog.xml final dans la solution.

4. Créer un fragment avec des vues

Dans Compose, vous créez des mises en page déclaratives à l'aide de Kotlin ou de Java. Vous pouvez accéder à différents "écrans" en parcourant différents composables, généralement dans la même activité. Lorsque vous créez une application avec des vues, un fragment qui héberge la mise en page XML remplace le concept de composable "écran".

Dans cette section, vous allez créer un Fragment pour héberger la mise en page fragment_entry_dialog et fournir des données à l'UI.

  1. Dans le package juicetracker, créez une classe intitulée EntryDialogFragment.
  2. Faites en sorte que EntryDialogFragment étende BottomSheetDialogFragment.

EntryDialogFragment.kt

import com.google.android.material.bottomsheet.BottomSheetDialogFragment

class EntryDialogFragment : BottomSheetDialogFragment() {
}

DialogFragment est un Fragment qui affiche une boîte de dialogue flottante. BottomSheetDialogFragment hérite de la classe DialogFragment, mais affiche une feuille correspondant à la largeur de l'écran et épinglée en bas. Cette approche correspond à la conception illustrée précédemment.

  1. Recompilez le projet. Les fichiers de liaison de vue basés sur la mise en page fragment_entry_dialog sont alors générés automatiquement. Les liaisons de vue vous permettent d'accéder aux View XML déclarées et d'interagir avec elles. Pour en savoir plus, consultez cette documentation.
  2. Dans la classe EntryDialogFragment, implémentez la fonction onCreateView(). Comme son nom l'indique, cette fonction crée la View pour ce Fragment.

EntryDialogFragment.kt

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
): View? {
   return super.onCreateView(inflater, container, savedInstanceState)
}

La fonction onCreateView() renvoie une View, mais pour le moment, elle ne renvoie pas de View utile.

  1. Renvoyez la View générée en gonflant le FragmentEntryDialogViewBinding au lieu de renvoyer super.onCreateView().

EntryDialogFragment.kt

import com.example.juicetracker.databinding.FragmentEntryDialogBinding

override fun onCreateView(
   inflater: LayoutInflater,
   container: ViewGroup?,
   savedInstanceState: Bundle?
): View? {
   return FragmentEntryDialogBinding.inflate(inflater, container, false).root
}
  1. En dehors de la fonction onCreateView(), mais dans la classe EntryDialogFragment, créez une instance d'EntryViewModel.
  2. Implémentez la fonction onViewCreated().

Après avoir gonflé la liaison de vue, vous pouvez accéder aux Views de la mise en page et les modifier. La méthode onViewCreated() est appelée après onCreateView() dans le cycle de vie. La méthode onViewCreated() est recommandée pour accéder aux Views et les modifier dans la mise en page.

  1. Créez une instance de la liaison de vue en appelant la méthode bind() sur FragmentEntryDialogBinding.

À ce stade, votre code devrait se présenter comme suit :

EntryDialogFragment.kt

import androidx.fragment.app.viewModels
import com.example.juicetracker.ui.AppViewModelProvider
import com.example.juicetracker.ui.EntryViewModel

class EntryDialogFragment : BottomSheetDialogFragment() {

   private val entryViewModel by viewModels<EntryViewModel> { AppViewModelProvider.Factory }

   override fun onCreateView(
       inflater: LayoutInflater,
       container: ViewGroup?,
       savedInstanceState: Bundle?
   ): View {
       return FragmentEntryDialogBinding.inflate(inflater, container, false).root
   }

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
    }
}

Vous pouvez accéder aux vues et les définir via la liaison. Par exemple, vous pouvez définir un objet TextView via la méthode setText().

binding.name.setText("Apple juice")

L'interface utilisateur de la boîte de dialogue de saisie permet à l'utilisateur de créer un élément, mais vous pouvez également l'utiliser pour modifier un élément existant. Par conséquent, le fragment doit récupérer un élément sur lequel l'utilisateur a cliqué. Le composant Navigation facilite l'accès à EntryDialogFragment et la récupération d'un élément sur lequel l'utilisateur a cliqué.

L'élément EntryDialogFragment n'est pas encore terminé, mais ne vous inquiétez pas ! Pour l'instant, passez à la section suivante pour découvrir comment utiliser le composant de navigation dans une application avec des Views.

5. Modifier le composant Navigation

Dans cette section, vous utiliserez le composant Navigation pour ouvrir la boîte de dialogue de saisie et pour récupérer un élément, le cas échéant.

Compose permet d'afficher différents composables rien qu'en les appelant. Toutefois, les fragments ne fonctionnent pas de la même manière. Le composant Navigation coordonne les "destinations" des fragments, ce qui permet de passer facilement d'un fragment à l'autre grâce aux vues qu'ils contiennent.

Utilisez le composant Navigation pour coordonner la navigation vers EntryDialogFragment.

  1. Ouvrez le fichier nav_graph.xml et assurez-vous que l'onglet Design (Conception) est sélectionné. 783cb5d7ff0ba127.png
  2. Cliquez sur l'icône 93401bf098936c15.png pour ajouter une destination.

d5410c90e408b973.png

  1. Sélectionnez la destination EntryDialogFragment. Cette action déclare l'élément entryDialogFragment dans le graphique de navigation, ce qui le rend accessible pour les actions de navigation.

418feed425072ea4.png

Vous devez lancer EntryDialogFragment à partir de TrackerFragment. Par conséquent, une action de navigation doit être effectuée pour accomplir cette tâche.

  1. Faites glisser le curseur au-dessus de trackerFragment. Sélectionnez le point gris et faites glisser la ligne vers entryDialogFragment. 85decb6fcddec713.png
  2. L'affichage Projet nav_graph vous permet de déclarer des arguments pour une destination. Pour ce faire, sélectionnez la destination et cliquez sur l'icône a0d73140a20e4348.png à côté du menu déroulant Arguments. Utilisez cette fonctionnalité pour ajouter un argument itemId de type Long à entryDialogFragment. La valeur par défaut devrait être 0L.

555cf791f64f62b8.png

840105bd52f300f7.png

Notez que TrackerFragment contient une liste d'éléments Juice. Si vous cliquez sur l'un de ces éléments, EntryDialogFragment s'ouvre.

  1. Recompilez le projet. L'argument itemId est désormais accessible dans EntryDialogFragment.

6. Terminer le fragment

À l'aide des données provenant des arguments de navigation, finalisez la boîte de dialogue de saisie.

  1. Récupérez navArgs() dans la méthode onViewCreated() d'EntryDialogFragment.
  2. Récupérez itemId à partir de navArgs().
  3. Implémentez saveButton pour enregistrer le jus ajouté ou modifié à l'aide de ViewModel.

À partir de l'interface utilisateur de la boîte de dialogue de saisie, indiquez via un rappel que la couleur par défaut est le rouge. Pour l'instant, transmettez cette information en tant qu'espace réservé.

Transmettez l'ID de l'élément à partir des arguments lors de l'appel de saveJuice().

EntryDialogFragment.kt

import androidx.navigation.fragment.navArgs
import com.example.juicetracker.data.JuiceColor

class EntryDialogFragment : BottomSheetDialogFragment() {

   //...
   var selectedColor: JuiceColor = JuiceColor.Red

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
        val args: EntryDialogFragmentArgs by navArgs()
        val juiceId = args.itemId

        binding.saveButton.setOnClickListener {
           entryViewModel.saveJuice(
               juiceId,
               binding.name.text.toString(),
               binding.description.text.toString(),
               selectedColor.name,
               binding.ratingBar.rating.toInt()
           )
        }
    }
}
  1. Une fois les données enregistrées, fermez la boîte de dialogue avec la méthode dismiss().

EntryDialogFragment.kt

class EntryDialogFragment : BottomSheetDialogFragment() {

    //...
    var selectedColor: JuiceColor = JuiceColor.Red
    //...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentEntryDialogBinding.bind(view)
        val args: EntryDialogFragmentArgs by navArgs()
        binding.saveButton.setOnClickListener {
           entryViewModel.saveJuice(
               juiceId,
               binding.name.text.toString(),
               binding.description.text.toString(),
               selectedColor.name,
               binding.ratingBar.rating.toInt()
           )
           dismiss()
        }
    }
}

Gardez à l'esprit que le code ci-dessus ne termine pas le EntryDialogFragment. Vous devez encore implémenter un plusieurs éléments, tels que le remplissage des champs avec des données Juice existantes (le cas échéant), la sélection d'une couleur à partir de colorSpinner, l'implémentation de cancelButton, et plus encore. Cependant, ce code n'est pas propre aux Fragments, et vous pouvez l'ajouter par vous-même. Essayez d'implémenter les autres fonctionnalités. En dernier recours, vous pouvez consulter le code de solution de cet atelier de programmation.

7. Ouvrir la boîte de dialogue de saisie

La dernière tâche consiste à ouvrir la boîte de dialogue de saisie à l'aide du composant Navigation. La boîte de dialogue de saisie doit s'ouvrir lorsque l'utilisateur clique sur le bouton d'action flottant. Elle doit également lancer et transmettre l'ID correspondant lorsque l'utilisateur clique sur un élément.

  1. Dans le onClickListener() du bouton d'action flottant, appelez navigate() au niveau du contrôleur de navigation.

TrackerFragment.kt

import androidx.navigation.findNavController

//...

binding.fab.setOnClickListener { fabView ->
   fabView.findNavController().navigate(
   )
}

//...
  1. Dans la fonction de navigation, transmettez l'action permettant de passer de l'outil de suivi à la boîte de dialogue de saisie.

TrackerFragment.kt

//...

binding.fab.setOnClickListener { fabView ->
   fabView.findNavController().navigate(
TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment()
   )
}

//...
  1. Répétez cette action dans le corps du lambda pour la méthode onEdit() dans JuiceListAdapter, mais cette fois, transmettez le id de Juice.

TrackerFragment.kt

//...

onEdit = { drink ->
   findNavController().navigate(
       TrackerFragmentDirections.actionTrackerFragmentToEntryDialogFragment(drink.id)
   )
},

//...

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

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.