1. Avant de commencer
Cet atelier de programmation présente une nouvelle application appelée Lunch Tray que vous allez créer vous-même. Il explique les différentes étapes à suivre pour mener à bien le projet d'application Lunch Tray, y compris sa configuration et ses tests dans Android Studio.
Cet atelier de programmation est différent des autres ateliers de ce cours. Contrairement aux précédents ateliers de programmation, celui-ci n'a pas pour objectif de vous fournir un tutoriel détaillé sur la création d'une application. Au lieu de cela, il vous aide à configurer un projet que vous devrez réaliser de manière indépendante, avec les instructions nécessaires pour finaliser une application et vérifier vous-même votre travail.
Au lieu du code de solution, nous proposons une suite de tests intégrés à l'application que vous téléchargerez. Vous exécuterez ces tests dans Android Studio (nous vous montrerons comment procéder plus tard dans cet atelier) et vérifierez si votre code passe ces tests avec succès. Plusieurs tentatives seront peut-être nécessaires. Même les développeurs professionnels ne réussissent pas tous les tests dès le premier essai. Une fois que votre code aura réussi tous les tests, vous pourrez considérer ce projet comme terminé.
Nous sommes conscients que vous pourriez préférer avoir directement accès à la solution. Toutefois, nous ne vous fournissons pas le code de la solution, car nous souhaitons que vous vous mettiez à la place d'un développeur professionnel. Peut-être devrez-vous renforcer des compétences différentes que vous ne maîtrisez pas encore, par exemple :
- Apprendre des termes, messages d'erreur et extraits de code dans l'application que vous ne reconnaissez pas.
- Tester le code, lire les erreurs, puis modifier le code et le tester à nouveau.
- Réviser le contenu précédent sur les bases d'Android afin de rafraîchir vos connaissances.
- Comparer le code dont vous savez qu'il fonctionne (code fourni dans le projet ou code de solution d'autres applications du module 3) avec le code que vous écrivez.
Cette nouvelle tâche peut sembler intimidante au premier abord, mais nous avons la certitude que si vous avez mené à bien le module 3, vous êtes fin prêt pour ce projet. Prenez votre temps et n'abandonnez pas. Faites-vous confiance !
Conditions préalables
- Ce projet est destiné aux utilisateurs qui ont terminé le module 3 du cours sur les bases d'Android en Kotlin.
Objectifs de l'atelier
- Vous utiliserez une application de commande de repas appelée "Lunch Tray", implémenterez un modèle de vue avec une liaison de données et ajouterez une navigation entre les fragments.
Ce dont vous avez besoin
- Un ordinateur sur lequel est installé Android Studio
2. Présentation de l'application terminée
Bienvenue dans le projet Lunch Tray !
Comme vous le savez probablement, la navigation est un élément fondamental du développement Android. Que vous utilisiez une application pour parcourir des recettes, trouver l'itinéraire vers votre restaurant préféré ou, surtout, commander des plats, il y a des chances que vous deviez parcourir plusieurs écrans de contenu. Dans ce projet, vous mettrez à profit les compétences que vous avez acquises dans le module 3 pour créer une application de commande de repas, appelée "Lunch Tray". Elle vous permettra d'implémenter un modèle de vue, des liaisons de données et la navigation entre les écrans.
Vous trouverez ci-dessous les captures d'écran finales de l'application. Lors du premier lancement de l'application Lunch Tray, l'utilisateur est invité à accéder à un écran comportant un seul bouton intitulé "Lancer la commande".
Après avoir cliqué sur Lancer la commande, l'utilisateur peut sélectionner un participant parmi les options disponibles. Il peut modifier sa sélection, ce qui met à jour le sous-total indiqué en bas.
L'écran suivant permet à l'utilisateur d'ajouter un accompagnement.
L'écran suivant permet à l'utilisateur de sélectionner un accompagnement pour sa commande.
Enfin, l'utilisateur voit un récapitulatif du coût de sa commande, qui comprend le sous-total, la taxe de vente et le coût total. Il peut également envoyer ou annuler la commande.
Dans les deux cas, l'utilisateur est redirigé vers le premier écran. Si l'utilisateur a envoyé la commande, un toast devrait s'afficher en bas de l'écran pour confirmer que cette opération a bien été effectuée.
3. Premiers pas
Télécharger le code du projet
Notez que le nom du dossier est android-basics-kotlin-lunch-tray-app
. Sélectionnez ce dossier lorsque vous ouvrez le projet dans Android Studio.
- Accédez à la page du dépôt GitHub fournie pour le projet.
- Vérifiez que le nom de la branche correspond à celui spécifié dans l'atelier de programmation. Par exemple, dans la capture d'écran suivante, le nom de la branche est main.
- Sur la page GitHub du projet, cliquez sur le bouton Code pour afficher une fenêtre pop-up.
- Dans la fenêtre pop-up, cliquez sur le bouton Download ZIP (Télécharger le fichier ZIP) pour enregistrer le projet sur votre ordinateur. Attendez la fin du téléchargement.
- Recherchez le fichier sur votre ordinateur (il se trouve probablement dans le dossier Téléchargements).
- Double-cliquez sur le fichier ZIP pour le décompresser. Un dossier contenant les fichiers du projet est alors créé.
Ouvrir le projet dans Android Studio
- Lancez Android Studio.
- Dans la fenêtre Welcome to Android Studio (Bienvenue dans Android Studio), cliquez sur Open (Ouvrir).
Remarque : Si Android Studio est déjà ouvert, sélectionnez l'option de menu File > Open (Fichier > Ouvrir).
- Dans l'explorateur de fichiers, accédez à l'emplacement du dossier du projet décompressé (il se trouve probablement dans le dossier Téléchargements).
- Double-cliquez sur le dossier de ce projet.
- Attendez qu'Android Studio ouvre le projet.
- Cliquez sur le bouton Run (Exécuter) pour créer et exécuter l'application. Assurez-vous qu'elle fonctionne correctement.
Avant de commencer à implémenter ViewModel
et la navigation, prenez le temps de vérifier que le projet a bien été compilé et de vous familiariser avec celui-ci. Lorsque vous exécutez l'application pour la première fois, un écran vide s'affiche. MainActivity
ne présente aucun fragment, car vous n'avez pas encore configuré le graphique de navigation.
La structure du projet doit être semblable à celle des autres projets sur lesquels vous avez travaillé. Des packages distincts sont fournis pour les données, le modèle et l'interface utilisateur, ainsi que des répertoires distincts pour les ressources.
Toutes les options de déjeuner que l'utilisateur peut commander (plats principaux, accompagnements et garnitures) sont représentées par la classe MenuItem
dans le package model. Les objets MenuItem
ont un nom, une description, un prix et un type.
data class MenuItem(
val name: String,
val description: String,
val price: Double,
val type: Int
) {
fun getFormattedPrice(): String = NumberFormat.getCurrencyInstance().format(price)
}
Le type est représenté par un entier provenant de l'objet ItemType
dans le package constants.
object ItemType {
val ENTREE = 1
val SIDE_DISH = 2
val ACCOMPANIMENT = 3
}
Les objets MenuItem
individuels se trouvent dans DataSource.kt
dans le package de données.
object DataSource {
val menuItems = mapOf(
"cauliflower" to
MenuItem(
name = "Cauliflower",
description = "Whole cauliflower, brined, roasted, and deep fried",
price = 7.00,
type = ItemType.ENTREE
),
...
}
Cet objet contient simplement une carte composée d'une clé et d'un élément MenuItem
correspondant. Vous accéderez à DataSource
depuis ObjectViewModel
, que vous implémenterez en premier.
Définir le modèle de vue
Comme illustré dans les captures d'écran de la page précédente, l'application demande à l'utilisateur trois choses : un plat, un accompagnement et une garniture. L'écran de récapitulatif de la commande affiche ensuite un sous-total et calcule la taxe de vente en fonction des articles sélectionnés, qui serviront à calculer le total de la commande.
Dans le package model, ouvrez OrderViewModel.kt
. Vous verrez que quelques variables y sont déjà définies. La propriété menuItems
vous permet simplement d'accéder à DataSource
à partir de ViewModel
.
val menuItems = DataSource.menuItems
Tout d'abord, il existe des variables pour previousEntreePrice
, previousSidePrice
et previousAccompanimentPrice
. Étant donné que le sous-total est mis à jour au fur et à mesure que l'utilisateur fait son choix (plutôt que d'être ajouté à la fin), ces variables sont utilisées pour suivre la sélection précédente de l'utilisateur s'il la modifie avant de passer à l'écran suivant. Vous les utiliserez pour vérifier que le sous-total reflète la différence entre le prix des articles précédents et celui des articles actuellement sélectionnés.
private var previousEntreePrice = 0.0
private var previousSidePrice = 0.0
private var previousAccompanimentPrice = 0.0
Il existe également des variables privées, _entree
, _side
et _accompaniment
, pour stocker l'article sélectionné. Elles sont de type MutableLiveData<MenuItem?>
. Chacune d'elles est accompagnée d'une propriété de support publique, entree
, side
et accompaniment
, de type immuable LiveData<MenuItem?>
. Elles sont accessibles en fonction des mises en page des fragments pour afficher l'élément sélectionné à l'écran. La valeur MenuItem
contenue dans l'objet LiveData
peut également être nulle, car l'utilisateur peut ne pas sélectionner de plat principal, d'accompagnement, ni de garniture.
// Entree for the order
private val _entree = MutableLiveData<MenuItem?>()
val entree: LiveData<MenuItem?> = _entree
// Side for the order
private val _side = MutableLiveData<MenuItem?>()
val side: LiveData<MenuItem?> = _side
// Accompaniment for the order.
private val _accompaniment = MutableLiveData<MenuItem?>()
val accompaniment: LiveData<MenuItem?> = _accompaniment
Il existe également des variables LiveData
pour le sous-total, le total et les taxes. Elles utilisent le format numérique afin d'être affichées sous la forme d'une devise.
// Subtotal for the order
private val _subtotal = MutableLiveData(0.0)
val subtotal: LiveData<String> = Transformations.map(_subtotal) {
NumberFormat.getCurrencyInstance().format(it)
}
// Total cost of the order
private val _total = MutableLiveData(0.0)
val total: LiveData<String> = Transformations.map(_total) {
NumberFormat.getCurrencyInstance().format(it)
}
// Tax for the order
private val _tax = MutableLiveData(0.0)
val tax: LiveData<String> = Transformations.map(_tax) {
NumberFormat.getCurrencyInstance().format(it)
}
Enfin, le taux de taxe est une valeur codée en dur correspondant à 0,08 (8 %).
private val taxRate = 0.08
Vous devez implémenter six méthodes dans OrderViewModel
.
setEntree(), setSide() et setAccompaniment()
Toutes ces méthodes devraient fonctionner de la même manière pour le plat principal, l'accompagnement et la garniture, respectivement. Par exemple, setEntree()
doit fonctionner comme suit :
- Si
_entree
n'est pasnull
(c'est-à-dire si l'utilisateur a déjà sélectionné un plat, mais qu'il a modifié son choix), définissezpreviousEntreePrice
sur le prix decurrent _entree
. - Si l'élément
_subtotal
n'est pasnull
, soustrayezpreviousEntreePrice
du sous-total. - Remplacez la valeur de
_entree
par le plat principal transmis à la fonction (accédez àMenuItem
à l'aide demenuItems
). - Appelez
updateSubtotal()
en transmettant le prix du plat principal que vous venez de sélectionner.
La logique pour setSide()
et setAccompaniment()
est identique à l'implémentation de setEntree()
.
updateSubtotal()
updateSubtotal()
est appelé avec un argument pour générer le nouveau prix qui doit être ajouté au sous-total. Cette méthode doit effectuer trois opérations :
- Si
_subtotal
n'est pasnull
, ajoutezitemPrice
à_subtotal
. - Sinon, si
_subtotal
correspond ànull
, définissez_subtotal
suritemPrice
. - Une fois que
_subtotal
a été défini (ou mis à jour), appelezcalculateTaxAndTotal()
pour que ces valeurs soient mises à jour et reflètent le nouveau sous-total.
calculateTaxAndTotal()
calculateTaxAndTotal()
doit mettre à jour les variables pour la taxe et le total en fonction du sous-total. Implémentez la méthode comme suit :
- Définissez la valeur de
_tax
sur le taux de taxe multiplié par le sous-total. - Définissez
_total
sur le sous-total plus les taxes.
resetOrder()
resetOrder()
est appelé lorsque l'utilisateur envoie ou annule une commande. Vous voulez vous assurer qu'il ne reste aucune donnée sur l'application lorsque l'utilisateur passe une nouvelle commande.
Pour implémenter resetOrder()
, rétablissez toutes les variables que vous avez modifiées dans OrderViewModel
sur leur valeur d'origine (0.0 ou null).
Créer des variables de liaison de données
Implémentez la liaison de données dans les fichiers de mise en page. Ouvrez les fichiers de mise en page, puis ajoutez des variables de liaison de données de type OrderViewModel
et/ou la classe de fragment correspondante.
Vous devez implémenter tous les commentaires TODO
pour définir les écouteurs de texte et de clics dans quatre fichiers de mise en page :
fragment_entree_menu.xml
fragment_side_menu.xml
fragment_accompaniment_menu.xml
fragment_checkout.xml
Chaque tâche est notée dans un commentaire TODO dans les fichiers de mise en page, mais les étapes sont résumées ci-dessous.
- Dans
fragment_entree_menu.xml
, dans la balise<data>
, ajoutez une variable de liaison pourEntreeMenuFragment
. Pour chacune des cases d'option, vous devez définir le plat principal dans la vieViewModel
lorsqu'elle est sélectionnée. Le texte du sous-total doit être mis à jour en conséquence. Vous devez également définir l'attributonClick
pourcancel_button
etnext_button
de sorte à annuler la commande ou à passer à l'écran suivant. - Procédez de la même manière dans
fragment_side_menu.xml
, en ajoutant une variable de liaison pourSideMenuFragment
, sauf pour définir l'accompagnement dans le modèle de vue lorsque chaque case d'option est sélectionnée. Le texte du sous-total doit également être mis à jour, et vous devez aussi définir l'attributonClick
pour les boutons "Annuler" et "Suivant". - Répétez l'opération, mais dans
fragment_accompaniment_menu.xml
, avec une variable de liaison pourAccompanimentMenuFragment
, définissant la garniture lorsque chaque case d'option est sélectionnée. Là encore, vous devez également définir les attributs du texte du sous-total, des boutons "Annuler" et "Suivant". - Dans
fragment_checkout.xml
, vous devez ajouter une balise<data>
pour pouvoir définir des variables de liaison. Dans la balise<data>
, ajoutez deux variables de liaison, l'une pourOrderViewModel
et l'autre pourCheckoutFragment
. Dans les vues de texte, vous devez définir le nom et le prix du plat principal, de l'accompagnement et de la garniture sélectionnés à partir de la vueOrderViewModel
. Vous devez également définir le sous-total, les taxes et le total à partir de la vueOrderViewModel
. Ensuite, définissezonClickAttributes
pour le moment où la commande est envoyée, ainsi que pour le moment où la commande est annulée, à l'aide des fonctions appropriées issues deCheckoutFragment
.
.
Initialiser les variables de liaison de données dans les fragments
Initialisez les variables de liaison de données dans les fichiers de fragment correspondants au sein de la méthode onViewCreated()
.
EntreeMenuFragment
SideMenuFragment
AccompanimentMenuFragment
CheckoutFragment
Créer le graphique de navigation
Comme vous l'avez vu dans le module 3, un graphique de navigation est hébergé dans une vue FragmentContainerView
, contenue dans une activité. Ouvrez activity_main.xml
et remplacez TODO par le code suivant pour déclarer une vue FragmentContainerView
.
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Le graphique de navigation mobile_navigation.xml
se trouve dans le package res.navigation.
Il s'agit du graphique de navigation de l'application. Toutefois, le fichier est actuellement vide. Votre tâche consiste à ajouter des destinations au graphique de navigation et à modéliser la navigation suivante entre les écrans.
- Navigation de
StartOrderFragment
versEntreeMenuFragment
- Navigation de
EntreeMenuFragment
versSideMenuFragment
- Navigation de
SideMenuFragment
versAccompanimentMenuFragment
- Navigation de
AccompanimentMenuFragment
versCheckoutFragment
- Navigation de
CheckoutFragment
versStartOrderFragment
- Navigation de
EntreeMenuFragment
versStartOrderFragment
- Navigation de
SideMenuFragment
versStartOrderFragment
- Navigation de
AccompanimentMenuFragment
versStartOrderFragment
- La destination de départ doit correspondre à
StartOrderFragment
Une fois que vous avez configuré le graphique de navigation, vous devez effectuer la navigation dans les classes de fragment. Implémentez les commentaires TODO
restants dans les fragments, ainsi que dans MainActivity.kt
.
- Pour la méthode
goToNextScreen()
dansEntreeMenuFragment
,SideMenuFragment
etAccompanimentMenuFragment
, accédez à l'écran suivant dans l'application. - Pour la méthode
cancelOrder()
dansEntreeMenuFragment
,SideMenuFragment
,AccompanimentMenuFragment
etCheckoutFragment
, appelez d'abordresetOrder()
au niveau de la vuesharedViewModel
, puis accédez àStartOrderFragment
. - Dans
StartOrderFragment
, implémentezsetOnClickListener()
pour accéder àEntreeMenuFragment
. - Dans
CheckoutFragment
, implémentez la méthodesubmitOrder()
. AppelezresetOrder()
au niveau de la vuesharedViewModel
, puis accédez àStartOrderFragment
. - Enfin, dans
MainActivity.kt
, définisseznavController
surnavController
à partir deNavHostFragment
.
4. Tester votre application
Le projet "Lunch Tray" contient une cible "androidTest" avec plusieurs scénarios de test : MenuContentTests
, NavigationTests
et OrderFunctionalityTests
.
Exécuter vos tests
Pour exécuter vos tests, vous pouvez effectuer l'une des opérations suivantes :
Pour un scénario de test unique, ouvrez une classe de scénario de test et cliquez sur la flèche verte à gauche de la déclaration de classe. Vous pouvez ensuite sélectionner l'option "Run" (Exécuter) dans le menu. Tous les tests seront exécutés dans le scénario de test.
Souvent, il suffit d'exécuter un seul test, par exemple lorsqu'un test a échoué et que tous les autres ont réussi. Il est possible d'exécuter un seul test de la même manière que vous le feriez pour un scénario de test complet. Utilisez la flèche verte et sélectionnez l'option Exécuter.
Si vous avez plusieurs scénarios de test, vous pouvez également exécuter l'ensemble de la suite de tests. Comme pour l'exécution de l'application, cette option se trouve dans le menu Exécuter.
Notez qu'Android Studio utilise par défaut la dernière cible que vous avez exécutée (applications, cibles de test, etc.). Par conséquent, si le menu indique toujours Run > Run 'app' (Exécuter > Exécuter 'application'), vous pouvez exécuter la cible de test en sélectionnant Run > Run (Exécuter > Exécuter).
Sélectionnez ensuite la cible de test dans le menu pop-up.
5. Facultatif : faites-nous part de vos commentaires
Nous aimerions connaître votre avis sur ce projet. Répondez à cette courte enquête. Vos commentaires nous aideront à mener à bien les prochains projets de ce cours.