Peaufiner l'expérience utilisateur

1. Avant de commencer

Comme vous l'avez appris dans nos précédents ateliers de programmation, Material est un système de conception créé par Google. Ses consignes, composants et outils sont alignés sur les bonnes pratiques pour la création d'interfaces utilisateur. Dans cet atelier de programmation, vous allez mettre à jour l'application de calcul du pourboire (celles des ateliers de programmation précédents) pour améliorer l'expérience utilisateur, comme illustré dans la capture d'écran du résultat final ci-dessous. Vous allez également tester l'application dans d'autres scénarios pour vous assurer que l'expérience utilisateur est aussi fluide que possible.

5743ac5ee2493d7.png

Conditions préalables

  • Vous maîtrisez les widgets d'interface utilisateur courants, tels que TextView, ImageView, Button, EditText, RadioButton, RadioGroup et Switch.
  • Vous maîtrisez ConstraintLayout et le positionnement des vues enfants par définition des contraintes.
  • Vous maîtrisez la modification des mises en page XML.
  • Vous connaissez les différences entre les images bitmap et les drawables vectoriels.
  • Vous savez définir des attributs de thème dans un thème.
  • Vous savez activer le thème sombre sur un appareil.
  • Vous avez déjà modifié le fichier build.gradle de l'application pour les dépendances du projet.

Points abordés

  • Comment utiliser des composants Material Design dans votre application
  • Comment utiliser des icônes Material dans votre application en les important depuis Image Asset Studio
  • Comment créer et appliquer des styles
  • Comment définir des attributs de thème autres que les couleurs

Objectifs de l'atelier

  • Développer une appli de calcul de pourboire dont l'interface utilisateur (UI) est alignée sur les pratiques recommandées

Ce dont vous avez besoin

  • Un ordinateur exécutant la dernière version stable d'Android Studio
  • Le code pour l'application Tip Time obtenu en terminant les précédents ateliers de programmation de ce parcours et de ce parcours

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

Dans les ateliers précédents, vous avez créé l'application Tip Time, dont la fonction est de calculer un pourboire en fonction de ses options de personnalisation. L'UI de votre application ressemble actuellement à la capture d'écran ci-dessous. Elle est fonctionnelle, mais ressemble davantage à un prototype. Les champs ne semblent pas tout à fait alignés. La cohérence des styles et de l'espacement pourrait être nettement améliorée, de même que l'utilisation des composants Material Design.

6685eaafba30960a.png

3. Composants Material

Les composants Material sont des widgets d'interface utilisateur courants, qui facilitent l'implémentation du style Material dans votre application. La documentation explique comment utiliser et personnaliser les composants Material Design. Vous trouverez des consignes générales pour chaque composant Material Design, ainsi que des consignes spécifiques à la plate-forme Android pour les composants disponibles sur Android. Les diagrammes annotés fournissent suffisamment d'informations pour recréer un composant qui n'existerait pas sur la plate-forme de votre choix.

c4a4db857bb36c3f.png

Grâce aux composants Material, le fonctionnement de votre application sera plus cohérent avec celui des autres applications sur l'appareil de l'utilisateur. De cette façon, les modèles d'interface utilisateur appris dans une application peuvent être transposés vers la suivante. Les utilisateurs prendront en main votre application beaucoup plus rapidement. Il est recommandé d'utiliser les composants Material autant que possible (par opposition aux autres widgets). Ils sont également plus flexibles et personnalisables, comme démontré dans la prochaine tâche.

La bibliothèque de composants Material Design (MDC) doit être incluse en tant que dépendance dans votre projet. Cette ligne devrait déjà être présente dans votre projet par défaut. Dans le fichier build.gradle de votre application, assurez-vous que cette dépendance est incluse dans la dernière version de la bibliothèque. Pour en savoir plus, consultez la page Get started (premiers pas) du site Material.

app/build.gradle

dependencies {
    ...
    implementation 'com.google.android.material:material:<version>'
}

Champs de texte

Dans la disposition actuelle, en haut de votre application de calcul de pourboire se trouve un champ EditText pour saisir le coût du service. Ce champ EditText fonctionne, mais il ne respecte pas les dernières recommandations de Material Design concernant l'apparence et le comportement des champs de texte.

Chaque fois que vous prévoyez d'utiliser un nouveau composant, commencez par vous renseigner à son sujet sur le site Material. Le guide sur les champs de texte distingue deux types de champs de texte :

Champ de texte sur ligne

29fab63417a5e9ed.png

Champ de texte encadré

3f085f837e146150.png

Pour créer un champ de texte tel qu'illustré ci-dessus, utilisez un TextInputLayout pour encadrer un TextInputEditText issu de la bibliothèque MDC. Le champ de texte Material peut facilement être personnalisé pour :

  • afficher le texte saisi ou un libellé toujours visible ;
  • afficher une icône dans le champ de texte ;
  • afficher de l'aide ou des messages d'erreur.

Dans la première tâche de cet atelier de programmation, vous allez remplacer le EditText de coût du service par un champ de texte Material (composé de TextInputLayout et de TextInputEditText).

  1. Ouvrez l'application Tip Time dans Android Studio, puis accédez au fichier de mise en page activity_main.xml. Vous devriez y trouver un ConstraintLayout avec la mise en page de votre calculatrice de pourboire.
  2. Pour voir à quoi ressemble le code XML d'un champ de texte Material, reportez-vous aux conseils Android concernant les champs de texte. Les extraits doivent se présenter comme suit :
<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/textField"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/label">

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
    />

</com.google.android.material.textfield.TextInputLayout>
  1. Après avoir examiné cet exemple, insérez un champ de texte Material en tant que premier enfant de votre ConstraintLayout (avant le champ EditText). Le champ EditText sera supprimé par la suite.

Vous pouvez entrer cet élément dans Android Studio et utiliser la saisie semi-automatique pour vous faciliter la tâche. Vous pouvez également copier l'exemple de code XML depuis la page de documentation et le coller dans votre mise en page, comme suit. Notez que le TextInputLayout comporte une vue enfant, TextInputEditText. N'oubliez pas que les points de suspension (. . .) sont utilisés pour abréger les extraits, pour que vous puissiez vous concentrer sur les lignes XML modifiées.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    ...>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/label">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

    </com.google.android.material.textfield.TextInputLayout>

    <EditText
        android:id="@+id/cost_of_service" ... />

    ...

Vous devriez voir des erreurs dans l'élément TextInputLayout. Vous n'avez pas encore correctement délimité cette vue dans l'élément parent ConstraintLayout. De plus, la ressource de chaîne n'est pas reconnue. Vous allez corriger ces erreurs lors des prochaines étapes.

344a98d866c7f68c.png

  1. Ajoutez des contraintes verticales et horizontales pour le champ de texte, afin de le positionner correctement dans la ConstraintLayout parente. Comme vous n'avez pas encore supprimé le EditText, coupez les attributs EditText suivants et collez-les dans votre TextInputLayout : contraintes, ID de ressource cost_of_service, largeur 160dp, hauteur wrap_content, et texte d'invite @string/cost_of_service.
...

<com.google.android.material.textfield.TextInputLayout
   android:id="@+id/cost_of_service"
   android:layout_width="160dp"
   android:layout_height="wrap_content"
   android:hint="@string/cost_of_service"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintTop_toTopOf="parent">

   <com.google.android.material.textfield.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"/>

</com.google.android.material.textfield.TextInputLayout>

...

Vous verrez peut-être une erreur indiquant que l'ID cost_of_service est identique à l'ID de ressource de l'élément EditText. Vous pouvez ignorer cette erreur pour le moment. (EditText sera supprimée dans quelques étapes.)

  1. Assurez-vous ensuite que l'élément TextInputEditText présente tous les attributs appropriés. Coupez-collez le type d'entrée de EditText dans TextInputEditText. Remplacez l'ID de ressource de l'élément TextInputEditText par cost_of_service_edit_text.
<com.google.android.material.textfield.TextInputLayout ... >

   <com.google.android.material.textfield.TextInputEditText
       android:id="@+id/cost_of_service_edit_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:inputType="numberDecimal" />

</com.google.android.material.textfield.TextInputLayout>

La largeur de match_parent et la hauteur de wrap_content définies sont acceptables en l'état. Lorsque vous définissez une largeur sur match_parent, l'élément TextInputEditText aura la même largeur que son parent TextInputLayout (ici, 160dp).

  1. Maintenant que vous avez copié toutes les informations pertinentes de l'élément EditText, supprimez EditText de la mise en page.
  2. La vue Design (Conception) de votre mise en page devrait présenter cet aperçu. Le champ "Cost of service" (coût du service) ressemble désormais à un champ de texte Material.

28582b8e4103beeb.png

  1. Vous ne pouvez pas encore exécuter l'application, car votre fichier MainActivity.kt comporte une erreur dans la méthode calculateTip(). Comme nous l'avons vu précédemment dans un atelier de programmation, lorsque View Binding est activé pour votre projet, Android crée des propriétés dans un objet de liaison basé sur le nom de l'ID de ressource. Le champ servant à obtenir le coût du service a changé dans la mise en page XML et le code Kotlin doit être mis à jour en conséquence.

Les entrées utilisateur seront à désormais récupérées depuis l'élément TextInputEditText avec l'ID de ressource cost_of_service_edit_text. Dans MainActivity, utilisez binding.costOfServiceEditText pour accéder à la chaîne de texte qu'il contient. Le reste de la méthode calculateTip() peut rester le même.

private fun calculateTip() {
    // Get the decimal value from the cost of service text field
    val stringInTextField = binding.costOfServiceEditText.text.toString()
    val cost = stringInTextField.toDoubleOrNull()

    ...
}
  1. Beau travail ! Exécutez maintenant l'application et vérifiez qu'elle fonctionne toujours. Notez que le libellé "Cost of service" s'affiche désormais au-dessus de votre saisie à mesure que vous entrez du texte. Le pourboire devrait toujours être calculé comme prévu.

b4a27e58f63417b7.png

Commutateurs

Vous trouverez des conseils pour utiliser les commutateurs dans les consignes Material Design. Un commutateur est un widget que vous pouvez activer ou désactiver.

  1. Consultez les recommandations d'Android concernant les commutateurs Material. Vous y trouverez des informations sur le widget SwitchMaterial de la bibliothèque MDC, qui fournit des styles Material pour les commutateurs. Plus loin dans le guide, vous trouverez quelques exemples de code XML.
  2. Pour utiliser SwitchMaterial, vous devez explicitement spécifier SwitchMaterial dans votre mise en page et utiliser le nom de chemin complet.

Dans la mise en page activity_main.xml, remplacez la balise XML Switch par com.google.android.material.switchmaterial.SwitchMaterial.

...

<com.google.android.material.switchmaterial.SwitchMaterial
    android:id="@+id/round_up_switch"
    android:layout_width="0dp"
    android:layout_height="wrap_content" ... />

...
  1. Exécutez l'application pour vérifier sa compilation. Il n'y a aucune modification visible dans l'application. Toutefois, utiliser le SwitchMaterial de la bibliothèque MDC (au lieu du Switch de la plate-forme Android) présente un avantage : lorsque l'implémentation de la bibliothèque pour SwitchMaterial est mise à jour (par exemple, si les consignes Material Design changent), vous obtenez le widget mis à jour sans frais et sans aucune modification nécessaire de votre part. Cela renforce l'évolutivité de votre application.

À ce stade, vous avez vu deux avantages des composants Material Design prêts à l'emploi pour votre UI, et comment cela permet de rendre votre application plus conforme aux consignes Material. N'oubliez pas que vous pouvez toujours explorer d'autres composants Material Design fournis sur Android sur ce site.

4. Icônes

Les icônes sont des symboles qui aident les utilisateurs à comprendre l'interface utilisateur en communiquant visuellement la fonction souhaitée. Elles s'inspirent souvent d'objets du monde physique que l'utilisateur est supposé connaître. La conception des icônes réduit souvent le niveau de détail au minimum requis pour qu'elles soient reconnues par l'utilisateur. Par exemple, dans le monde physique, un crayon sert à écrire. Une icône de crayon symbolise généralement la création, l'ajout ou la modification d'un élément.

Crayon taillé et taille crayon sur un carnet ouvert. Photo par Angelina Litvin, publiée sur Unsplash

Icône de crayon en noir et blanc

Les icônes sont parfois liées à des objets devenus obsolètes dans le monde physique, comme l'icône de disquette. Cette icône omniprésente indique que vous pouvez enregistrer un fichier ou un élément de base de données. Cependant, si les disquettes étaient populaires dans les années 1970, elles ne sont que rarement utilisées depuis les années 2000. Mais l'utilisation persistante du symbole témoigne de la capacité d'un élément visuel fort à dépasser la durée de vie de sa forme physique.

Disquette posée à platPhoto par Vincent Botta, publiée sur Unsplash

Icône de disquette

Représenter les icônes dans votre application

Pour les icônes de votre application, plutôt que fournir différentes versions d'une image bitmap pour les différentes densités d'écran, il est recommandé d'utiliser des drawables vectoriels. Au lieu d'enregistrer les pixels qui constituent une image, les drawables vectoriels sont représentés par des fichiers XML qui stockent les instructions permettant de créer cette image. Vous pouvez augmenter ou diminuer la taille des images issues de drawables vectoriels sans perdre en qualité visuelle ni augmenter la taille du fichier.

Icônes fournies

Material Design propose un certain nombre d'icônes classées dans des catégories courantes répondant à la plupart des besoins. Voir la liste des icônes

bfdb896506790c69.png

Ces icônes peuvent également être personnalisées à l'aide de couleurs et de thèmes : Filled (plein), Outlined (contours), Rounded (arrondi), Two-Tone (bicolore) and Sharp (net).

Plein

Contours

Arrondi

Bicolore

Net

Ajouter des icônes

Dans cette tâche, vous allez ajouter trois icônes drawables vectorielles à l'application :

  1. Une icône à côté du champ de texte dédié au coût du service
  2. Une icône à côté de la question sur la qualité du service
  3. Une icône à côté de l'invite pour arrondir le montant du pourboire

La capture d'écran ci-dessous présente la version finale de l'application. Une fois les icônes ajoutées, vous pouvez ajuster la mise en page en fonction de leur position. Notez que les champs et le bouton de calcul sont décalés vers la droite par l'ajout des icônes.

8c4225390dd1fb20.png

Ajouter des drawables vectoriels comme ressources

Vous pouvez créer ces icônes sous forme de drawable vectoriel directement depuis Asset Studio dans Android Studio.

  1. Ouvrez l'onglet Resource Manager (Gestionnaire de ressources) situé à gauche de la fenêtre de l'application.
  2. Cliquez sur l'icône +, puis sélectionnez Vector Asset (Élément vectoriel).

6dabda0f4bc1f6ed.png

  1. Dans le champ Asset Type (Type d'élément), assurez-vous que la case d'option Clip Art (Image clipart) est sélectionnée.

914786d2d8b4025.png

  1. Cliquez sur le bouton à côté de Clip Art pour sélectionner une autre image clipart. Dans la fenêtre qui s'affiche, saisissez "call made" (appel passé). Vous utiliserez cette icône en forme de flèche pour l'option servant à arrondir le montant du pourboire. Sélectionnez-la et cliquez sur OK.

e7f607e4f576d75c.png

  1. Renommez l'icône ic_round_up. Nous vous recommandons d'utiliser le préfixe "ic_" lorsque vous attribuez un nom à vos fichiers d'icônes. Vous pouvez conserver les dimensions (24 dp x 24 dp) et la couleur (000000, noir).
  2. Appuyez sur Next (Suivant).
  3. Acceptez l'emplacement par défaut du répertoire, puis cliquez sur Finish (Terminer).

200aed40ee987672.png

  1. Répétez les étapes 2 à 7 pour les deux autres icônes.
  • Pour la question sur la qualité du service : recherchez l'icône "room service" (service de chambre), puis enregistrez-la sous le nom ic_service.
  • Pour le coût du service : recherchez l'icône "store" (magasin), puis enregistrez-la sous le nom ic_store.
  1. Lorsque vous avez terminé, l'onglet Resource Manager devrait ressembler à la capture d'écran ci-dessous. Ces trois drawables vectoriels (ic_round_up, ic_service et ic_store) seront également répertoriés dans votre dossier res/drawable.

c2d8b22f0fb55ce0.png

Compatibilité avec les anciennes versions d'Android

Vous venez d'ajouter des drawables vectoriels à votre application, et il est important de noter qu'ils ne sont pris en charge par la plate-forme Android que depuis la version Android 5.0 (niveau d'API 21).

Selon la configuration du projet, la version minimale du SDK pour l'application Tip Time est au niveau d'API 19. Autrement dit, l'application peut s'exécuter sur les appareils exécutant la version 19 de la plate-forme Android, ou les versions ultérieures.

Pour que votre application fonctionne sur ces anciennes versions d'Android (on parle alors de rétrocompatibilité), ajoutez l'élément vectorDrawables au fichier build.gradle de votre application. Cela permet d'utiliser des drawables vectoriels sur des versions de la plate-forme antérieures au niveau d'API 21, plutôt que de les convertir au format PNG lorsque le projet est créé. En savoir plus

app/build.gradle

android {
  defaultConfig {
    ...
    vectorDrawables.useSupportLibrary = true
   }
   ...
}

Maintenant que votre projet est configuré correctement, vous pouvez ajouter les icônes dans la mise en page.

Insérer des icônes et positionner des éléments

Vous utiliserez ImageViews pour afficher les icônes dans l'application. Voici à quoi ressemblera votre interface utilisateur finale.

5ed07dfeb648bd62.png

  1. Ouvrez la mise en page activity_main.xml.
  2. Commencez par placer l'icône de magasin à côté du champ de texte réservé au coût du service. Insérez un nouveau ImageView en tant que premier enfant de ConstraintLayout, avant TextInputLayout.
<androidx.constraintlayout.widget.ConstraintLayout
   ...>

   <ImageView
       android:layout_width=""
       android:layout_height=""

   <com.google.android.material.textfield.TextInputLayout
       android:id="@+id/cost_of_service"
       ...
  1. Configurez les attributs appropriés pour que votre ImageView contienne l'icône ic_store. Définissez icon_cost_of_service comme ID. Orientez l'attribut app:srcCompat vers la ressource drawable @drawable/ic_store. Un aperçu de l'icône s'affiche à côté de cette ligne XML.

Définissez également android:importantForAccessibility="no", l'image n'étant utilisée qu'à des fins décoratives.

<ImageView
    android:id="@+id/icon_cost_of_service"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:importantForAccessibility="no"
    app:srcCompat="@drawable/ic_store" />

ImageView devrait renvoyer une erreur, car la vue n'est pas encore délimitée. C'est votre prochaine étape.

  1. Positionnez le icon_cost_of_service en deux temps. Commencez par ajouter des contraintes à votre ImageView (étape actuelle), puis mettez à jour les contraintes du TextInputLayout qui apparaît à côté (étape 5). Ce diagramme montre comment configurer les contraintes.

d982b1b1f0131630.png

Vous devez configurer votre ImageView de sorte à contraindre sa bordure de début à celle de la vue parent (app:layout_constraintStart_toStartOf="parent").

L'icône doit être centrée verticalement par rapport au champ de texte situé à côté. Pour ce faire, liez le haut de cette ImageView (layout_constraintTop_toTopOf) au sommet du champ de texte. Ensuite, liez le bas de cette ImageView (layout_constraintBottom_toBottomOf) au bas du champ de texte. Utilisez l'ID de ressource @id/cost_of_service pour faire référence au champ de texte. Par défaut, lorsqu'un même widget est soumis à deux contraintes dans la même dimension (par exemple, en haut et en bas), ces contraintes sont appliquées de la même manière. L'icône est alors centrée verticalement, par rapport au champ réservé au coût du service.

<ImageView
    android:id="@+id/icon_cost_of_service"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:importantForAccessibility="no"
    app:srcCompat="@drawable/ic_store"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@id/cost_of_service"
    app:layout_constraintBottom_toBottomOf="@id/cost_of_service" />

L'icône et le champ de texte se chevauchent toujours dans la vue Design. Ce problème sera corrigé à l'étape suivante.

  1. Avant l'ajout de l'icône, le champ de texte était positionné au début du parent. Vous devez maintenant le décaler vers la droite. Mettez à jour les contraintes du champ de texte cost_of_service par rapport à icon_cost_of_service.

bb55ea0cddaa2a12.png

Le bord de début de TextInputLayout doit correspondre au bord de fin de ImageView (@id/icon_cost_of_service). Pour espacer les deux vues, ajoutez une marge de début de 16dp à votre TextInputLayout.

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/cost_of_service"
    ...
    android:layout_marginStart="16dp"
    app:layout_constraintStart_toEndOf="@id/icon_cost_of_service">

    <com.google.android.material.textfield.TextInputEditText ... />

</com.google.android.material.textfield.TextInputLayout>

Une fois toutes ces modifications effectuées, l'icône devrait être correctement positionnée à côté du champ de texte.

23dcae5c3931903f.png

  1. Insérez ensuite l'icône en forme de cloche à côté de la question "How was the service?" (Qu'avez-vous pensé du service ?). TextView. Vous pouvez déclarer le ImageView n'importe où dans votre ConstraintLayout, mais votre mise en page XML sera plus facile à lire si vous insérez la nouvelle ImageView dans la mise en page XML après la TextInputLayout, mais avant la TextView service_question.

Attribuez l'ID de ressource @+id/icon_service_question à votre nouvelle ImageView. Définissez les contraintes appropriées sur l'ImageView et la TextView de la question de qualité du service.

38c2dcb4cb18b5a.png

Ajoutez également une marge supérieure de 16dp à service_question TextView, afin de laisser plus d'espace vertical entre la question de qualité et le champ de texte réservé au coût du service, situé au-dessus.

...

   <ImageView
        android:id="@+id/icon_service_question"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        app:srcCompat="@drawable/ic_service"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/service_question"
        app:layout_constraintBottom_toBottomOf="@id/service_question" />

    <TextView
        android:id="@+id/service_question"
        ...
        android:layout_marginTop="16dp"
        app:layout_constraintStart_toStartOf="@id/cost_of_service"
        app:layout_constraintTop_toBottomOf="@id/cost_of_service"/>

...
  1. À ce stade, la vue Design doit se présenter comme suit : Le champ réservé au coût et la question de qualité du service (et leurs icônes respectives) sont très bien, mais les cases d'option ont été déplacées. Elles ne sont pas alignées verticalement avec le contenu situé au-dessus.

578834f5bd3a2d2a.png

  1. Améliorez la position des cases d'option en les décalant vers la droite, sous la question sur la qualité du service. Pour ce faire, vous devrez mettre à jour une contrainte RadioGroup. Appliquez une contrainte au bord de début du RadioGroup jusqu'au bord de début de la TextView service_question. Les autres attributs du RadioGroup peuvent rester identiques.

bf454f3f1617024d.png

...

<RadioGroup
    android:id="@+id/tip_options"
    ...
    app:layout_constraintStart_toStartOf="@id/service_question">

...
  1. Ensuite, ajoutez l'icône ic_round_up à la mise en page, à côté du commutateur "Round up tip ?". Essayez d'abord par vous-même. Si vous rencontrez des difficultés, vous pouvez consulter le code XML ci-dessous. Vous pouvez attribuer l'ID de ressource icon_round_up à la nouvelle ImageView.
  2. Dans le fichier XML de mise en page, insérez une nouvelle ImageView après le RadioGroup, mais avant le widget SwitchMaterial.
  3. Attribuez l'ID de ressource icon_round_up à cette ImageView, puis définissez srcCompat sur le drawable de l'icône @drawable/ic_round_up. Alignez le début de l'ImageView au début du parent, puis centrez verticalement l'icône par rapport au SwitchMaterial.
  4. Modifiez le SwitchMaterial pour le positionner à côté de l'icône, avec une marge de début de 16dp. Voici à quoi doit ressembler le code XML obtenu pour icon_round_up et round_up_switch.
...

   <ImageView
        android:id="@+id/icon_round_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:importantForAccessibility="no"
        app:srcCompat="@drawable/ic_round_up"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@id/round_up_switch"
        app:layout_constraintBottom_toBottomOf="@id/round_up_switch" />

    <com.google.android.material.switchmaterial.SwitchMaterial
        android:id="@+id/round_up_switch"
        ...
        android:layout_marginStart="16dp"
        app:layout_constraintStart_toEndOf="@id/icon_round_up" />

...
  1. La vue Design doit se présenter comme suit. Les trois icônes sont correctement positionnées.

8781ecbd11859cc4.png

  1. Si vous comparez ce résultat avec la capture d'écran finale de l'application, vous remarquerez que le bouton de calcul a été décalé pour s'aligner verticalement avec le champ réservé au coût du service, la question de qualité du service, les cases d'options et l'invite à arrondir le montant du pourboire. Pour obtenir ce résultat, alignez le début du bouton de calcul avec le début du round_up_switch. Ajoutez également une marge verticale de 8dp entre le bouton de calcul et le commutateur situé au-dessus.

84348568e13d9e32.png

...

<Button
   android:id="@+id/calculate_button"
   ...
   android:layout_marginTop="8dp"
   app:layout_constraintStart_toStartOf="@id/round_up_switch" />

...
  1. Enfin, corrigez la position de tip_result en ajoutant 8dp à la marge supérieure de la TextView.

8e21f52be710340d.png

...

<TextView
   android:id="@+id/tip_result"
   ...
   android:layout_marginTop="8dp" />

...
  1. Ça fait beaucoup d'étapes, mais vous les avez réussies l'une après l'autre. Bien joué ! Aligner correctement les éléments d'une mise en page exige un travail minutieux, mais offre un résultat beaucoup plus attrayant. Exécutez l'application. Elle devrait ressembler à la capture d'écran ci-dessous. Grâce à l'alignement vertical et à l'espacement entre les éléments, l'ensemble ne paraît plus aussi encombré.

1f2ef2c0c9a9bdc7.png

Vous n'avez pas encore terminé. Vous avez peut-être remarqué que la taille et la couleur de police de la question sur la qualité du service et du montant du pourboire diffèrent de celles du texte des cases d'option et du commutateur. Dans la prochaine tâche, vous uniformiserez ces éléments en utilisant des styles et des thèmes.

5. Styles et thèmes

Un style est une collection de valeurs d'attributs de vue pour un type de widget donné. Par exemple, un style TextView peut spécifier la couleur de police, la taille de police et la couleur de l'arrière-plan, pour ne citer que quelques éléments. L'extraction de ces attributs dans un style vous permet de l'appliquer facilement à plusieurs vues de la mise en page tout en le conservant en un seul point.

Dans cette tâche, vous allez d'abord créer des styles pour l'affichage de texte, les cases d'option et les widgets de commutateurs.

Créer des styles

  1. S'il n'existe pas encore, créez un fichier nommé styles.xml dans le répertoire res > values. Pour ce faire, effectuez un clic droit sur le répertoire values, puis sélectionnez New > Values Resource File (Nouveau > Fichier de ressource de valeurs). Nommez-le styles.xml. Le nouveau fichier contiendra le code ci-dessous.
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
  1. Créez un style TextView pour établir une cohérence entre les éléments textuels dans l'ensemble de l'application. Définissez le style une fois dans styles.xml. Vous pouvez ensuite l'appliquer à toutes les TextViews de la mise en page. Bien que vous puissiez définir un style à partir de zéro, vous pouvez aussi travailler à partir d'un style TextView existant issu de la bibliothèque MDC.

Lorsque vous appliquez un style à un composant, vous devez généralement partir d'un style parent correspondant au type de widget que vous utilisez. Ce point est important pour deux raisons. Premièrement, cela garantit que toutes les valeurs par défaut importantes sont définies dans votre composant. Et deuxièmement, cela permet à votre style d'hériter des éventuelles modifications ultérieures apportées à ce style parent.

Vous pouvez nommer votre style comme vous le souhaitez, mais il existe une convention recommandée. Si vous partez d'un style Material parent, attribuez un nom parallèle à votre style en remplaçant MaterialComponents par le nom de votre application (TipTime). Cela transpose vos modifications dans un espace de noms dédié, ce qui évite les conflits en cas d'introduction de nouveaux styles par Material. Par exemple :

Le nom de votre style (Widget.TipTime.TextView) hérite du style parent (Widget.MaterialComponents.TextView).

Ajoutez ce code à votre fichier styles.xml, entre les balises d'ouverture et de fermeture resources.

<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
</style>
  1. Configurez votre style TextView de sorte qu'il remplace les attributs android:minHeight,android:gravity, et android:textAppearance..

android:minHeight impose une hauteur minimale de 48 dp à la TextView. Les consignes Material Design préconisent une hauteur minimale de 48 dp par ligne.

Vous pouvez centrer le texte de la TextView verticalement en définissant l'attribut android:gravity comme dans la capture d'écran ci-dessous. Le paramètre Gravity contrôle le positionnement du contenu dans une vue. Étant donné que le contenu textuel réel n'occupe pas toute la hauteur de 48 dp, la valeur center_vertical centre le texte dans la TextView verticalement (mais ne modifie pas sa position horizontale). Les autres valeurs Gravity autorisées sont center, center_horizontal, top et bottom. N'hésitez pas à tester les différentes valeurs pour voir l'effet sur le texte.

6a7ecc6a49a858e9.png

Définissez la valeur de l'attribut d'apparence du texte sur ?attr/textAppearanceBody1. TextAppearance regroupe un ensemble de styles prédéfinis qui déterminent la taille, les polices et d'autres propriétés du texte. Pour découvrir d'autres apparences de texte proposées par Material, consultez la liste des échelles typographiques.

<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
    <item name="android:minHeight">48dp</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
  1. Appliquez le style Widget.TipTime.TextView à la TextView service_question en ajoutant un attribut de style à chaque TextView dans activity_main.xml.
<TextView
    android:id="@+id/service_question"
    style="@style/Widget.TipTime.TextView"
    ... />

Avant l'application du style, TextView ressemblait à ce qui suit, en gris avec une petite taille de police :

4d54a3179f0c6f8d.png

Une fois le style appliqué, TextView ressemble à ceci. Cette TextView semble maintenant plus cohérente avec le reste de la mise en page.

416d3928f9c3d3de.png

  1. Appliquez ce même style Widget.TipTime.TextView à la TextView tip_result.
<TextView
    android:id="@+id/tip_result"
    style="@style/Widget.TipTime.TextView"
    ... />

3ebe16aa8c5bc010.png

  1. Le même style de texte doit être appliqué au libellé de texte du commutateur. Toutefois, vous ne pouvez pas appliquer un style TextView à un widget SwitchMaterial. Les styles TextView ne peuvent être appliqués qu'aux éléments TextViews. Par conséquent, vous devez créer un style pour le commutateur. Les attributs minHeight, gravity et textAppearance sont identiques. Ce qui diffère ici est le nom et le parent du style, car vous héritez désormais du style Switch de la bibliothèque MDC. Le nom de votre style doit également correspondre à celui du parent.

Le nom de votre style (Widget.TipTime.CompoundButton.Switch) hérite du style parent (Widget.MaterialComponents.CompoundButton.Switch.).

<style name="Widget.TipTime.CompoundButton.Switch" parent="Widget.MaterialComponents.CompoundButton.Switch">
   <item name="android:minHeight">48dp</item>
   <item name="android:gravity">center_vertical</item>
   <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>

Vous pouvez également spécifier des attributs supplémentaires spécifiques aux commutateurs sous ce style, mais cela n'est pas nécessaire dans le cas présent.

  1. Vous devez encore assurer la cohérence visuelle des éléments textuels des cases d'options. Vous ne pouvez pas appliquer un style TextView ou Switch à un widget RadioButton. Vous devez créer un style dédié aux cases d'options. Vous pouvez travailler à partir du style RadioButton de la bibliothèque MDC.

Lorsque vous créez ce style, ajoutez également une marge intérieure entre le texte de la case d'option et l'image du cercle. paddingStart est un nouvel attribut que vous n'avez pas encore utilisé. La marge intérieure ("padding", en anglais) correspond à la quantité d'espace entre le contenu et les limites d'une vue. L'attribut paddingStart définit la marge intérieure uniquement au début du composant. Observez la différence entre un paddingStart à 0 dp et à 8 dp sur une case d'option.

4c1aa37bbdadab1d.png

35a96c994b82539e.png

<style name="Widget.TipTime.CompoundButton.RadioButton"
parent="Widget.MaterialComponents.CompoundButton.RadioButton">
   <item name="android:paddingStart">8dp</item>
   <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
  1. (Facultatif) Créez un fichier dimens.xml pour améliorer la gestion des valeurs fréquemment utilisées. Vous pouvez créer le fichier de la même manière que pour le fichier styles.xml ci-dessus. Sélectionnez le répertoire des valeurs, effectuez un clic droit, puis sélectionnez New > Values Resource File.

Dans cette petite application, vous avez répété le paramètre de hauteur minimale deux fois, ce qui reste tout à fait gérable. Cependant, nous pourrions rapidement perdre le contrôle si 4, 6, 10 composants ou plus encore partageaient cette valeur. Devoir tous les modifier individuellement serait à la fois fastidieux et source d'erreurs. Vous pouvez créer un autre fichier de ressources utile dans res > values. Nommé dimens.xml, il contient des dimensions courantes que vous pouvez nommer. En standardisant les valeurs courantes sous forme de dimensions nommées, vous facilitez la gestion de votre application. Comme la taille de Tip Time ne le justifie pas, nous n'utiliserons pas cette ressource en dehors de cette étape facultative. Cependant, avec des applications plus complexes dans un environnement de production en collaboration avec une équipe de conception, dimens.xml vous permet de modifier ces valeurs facilement et fréquemment.

dimens.xml

<resources>
   <dimen name="min_text_height">48dp</dimen>
</resources>

Vous devez mettre à jour le fichier styles.xml pour utiliser directement @dimen/min_text_height au lieu de 48dp.

...
<style name="Widget.TipTime.TextView" parent="Widget.MaterialComponents.TextView">
    <item name="android:minHeight">@dimen/min_text_height</item>
    <item name="android:gravity">center_vertical</item>
    <item name="android:textAppearance">?attr/textAppearanceBody1</item>
</style>
...

Ajouter ces styles à vos thèmes

Vous avez peut-être remarqué que vous n'avez pas encore appliqué les nouveaux styles RadioButton et Switch à leurs widgets respectifs. En effet, vous allez utiliser les attributs de thème pour définir radioButtonStyle et switchStyle dans le thème de l'application. Commençons par définir ce qu'est un thème.

Un thème est une collection de ressources nommées (les attributs du thème) pouvant être référencées ultérieurement dans des styles, des mises en page, etc. Vous pouvez spécifier un thème pour l'ensemble d'une application, pour une activité ou pour une hiérarchie de vues, et pas seulement pour une View individuelle. Vous avez précédemment modifié le thème de l'application dans themes.xml en définissant des attributs de thème tels que colorPrimary et colorSecondary, qui sont utilisés à plusieurs endroits et dans différents composants de l'application.

radioButtonStyle et switchStyle sont d'autres attributs de thème que vous pouvez définir. Les ressources de style que vous fournissez pour ces attributs de thème sont appliquées à chaque case d'option et à chaque commutateur de la hiérarchie des vues à laquelle s'applique le thème.

Il existe également un attribut de thème pour textInputStyle, où la ressource de style spécifiée sera appliquée à tous les champs de saisie de texte de l'application. Pour que votre TextInputLayout apparaisse comme un champ de texte encadré (conformément aux consignes Material Design), le style OutlinedBox est défini sur Widget.MaterialComponents.TextInputLayout.OutlinedBox dans la bibliothèque MDC. C'est ce style que vous utiliserez.

2b2a5836a5d9bedf.png

  1. Modifiez le fichier themes.xml pour que le thème fasse référence aux styles souhaités. Pour définir un attribut de thème, procédez comme vous l'avez fait pour déclarer les attributs de thème colorPrimary et colorSecondary dans un atelier de programmation précédent. Cette fois-ci, les attributs de thème concernés sont textInputStyle, radioButtonStyle et switchStyle. Vous allez utiliser les styles que vous avez créés précédemment pour RadioButton et Switch, ainsi que le style du champ de texte Material OutlinedBox.

Copiez le code suivant dans res/values/themes.xml dans la balise de style du thème de votre application.

<item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
<item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
<item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
  1. Votre fichier res/values/themes.xml devrait se présenter comme suit. Si vous le souhaitez, vous pouvez ajouter des commentaires dans le fichier XML (délimités par <!- et -->).
<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Base application theme. -->
    <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Text input fields -->
        <item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
        <!-- Radio buttons -->
        <item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
        <!-- Switches -->
        <item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
    </style>

</resources>
  1. Assurez-vous d'apporter les mêmes modifications au thème sombre dans le fichier themes.xml (night). Votre fichier res/values-night/themes.xml devrait se présenter comme suit.
<resources xmlns:tools="http://schemas.android.com/tools">

    <!-- Application theme for dark theme. -->
    <style name="Theme.TipTime" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        ...
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Text input fields -->
        <item name="textInputStyle">@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox</item>
        <!-- For radio buttons -->
        <item name="radioButtonStyle">@style/Widget.TipTime.CompoundButton.RadioButton</item>
        <!-- For switches -->
        <item name="switchStyle">@style/Widget.TipTime.CompoundButton.Switch</item>
    </style>

</resources>
  1. Exécutez l'application et observez les changements. Le style OutlinedBox améliore considérablement le champ de saisie, et tous vos éléments textuels sont désormais cohérents.

31ac15991713b031.png 3e861407146c9ed4.png

6. Améliorer l'expérience utilisateur

Lorsque vous êtes près de finaliser votre application, vous devez tester non seulement avec le parcours d'utilisation attendu, mais aussi d'autres scénarios utilisateur. Vous constaterez que de petites modifications du code peuvent grandement améliorer l'expérience utilisateur.

Faire pivoter l'appareil

  1. Faites pivoter votre appareil en mode Paysage. Vous devrez peut-être commencer par activer le paramètre Rotation automatique. (Cette option se trouve dans les Réglages rapides de l'appareil. Vous pouvez également y accéder via Paramètres > Écran > Paramètres avancés > Rotation automatique de l'écran.)

f2edb1ae9926d5f1.png

Vous pouvez ensuite utiliser les options de l'émulateur (situées dans la partie supérieure droite à côté de l'appareil) pour faire pivoter l'écran vers la droite ou vers la gauche.

2bc08f73d28968cb.png

  1. Vous remarquerez que certains composants de l'interface utilisateur, y compris le bouton Calculate, sont tronqués. Ce qui empêche d'utiliser l'application !

d73499f9c9d2b330.png

  1. Pour résoudre ce bug, ajoutez une ScrollView autour de votre ConstraintLayout. Votre fichier XML devrait se présenter comme suit.
<ScrollView
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_height="match_parent"
   android:layout_width="match_parent">

   <androidx.constraintlayout.widget.ConstraintLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:padding="16dp"
       tools:context=".MainActivity">

       ...
   </ConstraintLayout>

</ScrollView>
  1. Exécutez et testez à nouveau l'application. Lorsque vous faites pivoter l'appareil en mode Paysage, vous devriez pouvoir faire défiler l'interface utilisateur pour accéder au bouton de calcul et voir le résultat. Ce correctif est utile non seulement pour le mode Paysage, mais aussi pour d'autres appareils Android dont les dimensions peuvent varier. Quelle que soit la taille de l'écran de l'appareil, l'utilisateur peut faire défiler la mise en page.

Masquer le clavier après la saisie

Vous avez peut-être remarqué qu'après avoir indiqué un coût de service, le clavier reste actif. Masquer manuellement le clavier à chaque fois, pour faciliter l'accès au bouton de calcul, est plutôt fastidieux. Pour résoudre ce problème, vous pouvez masquer automatiquement le clavier lorsque l'utilisateur appuie sur la touche Entrée.

e2c3a3dbc40218a2.png

Dans le champ de texte, vous pouvez définir un écouteur ("key listener") pour répondre aux événements lorsque l'utilisateur appuie sur certaines touches. Chaque option d'entrée d'un clavier, y compris la touche Enter, est associée à un code. Notez qu'un clavier à l'écran, aussi appelé clavier virtuel, doit être distingué d'un clavier physique.

1c95d7406d3847fe.png

Dans cette tâche, configurez un écouteur pour le champ de texte, de sorte à réagir à la touche Enter. Lorsque cet événement est détecté, masquez le clavier.

  1. Copiez et collez cette méthode d'assistance dans votre classe MainActivity. Vous pouvez l'insérer juste avant l'accolade fermante de la classe MainActivity. handleKeyEvent() est une fonction d'assistance privée qui masque le clavier virtuel si le paramètre d'entrée keyCode est égal à KeyEvent.KEYCODE_ENTER. InputMethodManager détermine si le clavier virtuel est affiché ou masqué, et permet à l'utilisateur de choisir le clavier à afficher. La méthode renvoie "true" si l'événement de touche a été traité et renvoie "false" dans le cas contraire.

MainActivity.kt

private fun handleKeyEvent(view: View, keyCode: Int): Boolean {
   if (keyCode == KeyEvent.KEYCODE_ENTER) {
       // Hide the keyboard
       val inputMethodManager =
           getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
       inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
       return true
   }
   return false
}
  1. Associez maintenant un écouteur de touches au widget TextInputEditText. N'oubliez pas que vous pouvez accéder au widget TextInputEditText via l'objet de liaison via binding.costOfServiceEditText.

Appelez la méthode setOnKeyListener() dans costOfServiceEditText et transmettez à un OnKeyListener. Cette méthode est semblable à celle utilisée pour définir un écouteur de clics sur le bouton de calcul dans l'application avec binding.calculateButton.setOnClickListener { calculateTip() }.

Le code permettant de définir un écouteur de touches sur une vue est un peu plus complexe. Dans les grandes lignes, OnKeyListener comporte une méthode onKey() qui se déclenche lorsqu'une touche est activée. La méthode onKey() comporte trois arguments d'entrée : la vue, le code de la touche activée et un événement de touche (vous ne l'utiliserez pas et pouvez donc le nommer "_"). Lorsque la méthode onKey() est appelée, vous devez appeler votre méthode handleKeyEvent(), et transmettre les arguments de la vue et du code de touche. La syntaxe d'écriture est view, keyCode, _ -> handleKeyEvent(view, keyCode). C'est ce qu'on appelle une expression lambda. Vous en apprendrez davantage sur les lambdas dans un module ultérieur.

Ajoutez le code permettant de configurer l'écouteur de touches sur le champ de texte dans la méthode onCreate() de l'activité. Vous devez procéder ainsi pour que votre écouteur de clé soit associé dès la création de la mise en page, avant que l'utilisateur commence à interagir avec l'activité.

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
   ...

   setContentView(binding.root)

   binding.calculateButton.setOnClickListener { calculateTip() }

   binding.costOfServiceEditText.setOnKeyListener { view, keyCode, _ -> handleKeyEvent(view, keyCode)
   }
}
  1. Vérifiez que vos nouvelles modifications fonctionnent. Exécutez l'application et saisissez un coût de service. Appuyez sur la touche Entrée du clavier pour le masquer.

Tester votre application avec TalkBack

L'objectif, tout au long de ce cours, est de créer des applications accessibles au plus grand nombre d'utilisateurs possible. Certains utilisateurs peuvent utiliser Talkback pour accéder à votre appli et la parcourir. TalkBack est le lecteur d'écran de Google intégré aux appareils Android. Cette fonctionnalité fournit des commentaires audio pour permettre d'utiliser un appareil sans regarder l'écran.

Vérifiez qu'un utilisateur peut effectuer tout le parcours de calcul du pourboire dans votre application lorsque TalkBack est activé.

  1. Activez TalkBack sur votre appareil en suivant ces instructions.
  2. Revenez à l'application Tip Time.
  3. Explorez votre application avec TalkBack en suivant ces instructions. Balayez l'écran vers la droite pour parcourir les éléments de l'écran dans l'ordre, puis vers la gauche pour procéder dans la direction opposée. Appuyez deux fois n'importe où pour effectuer une sélection. Vérifiez que vous pouvez accéder à tous les éléments de votre application à l'aide des gestes de balayage.
  4. Assurez-vous qu'un utilisateur de Talkback peut accéder à chaque élément à l'écran, saisir un coût de service, modifier les options de pourboire, calculer le pourboire et écouter le montant annoncé. Gardez à l'esprit qu'aucun commentaire audio n'est fourni pour les icônes, car vous les avez marquées comme importantForAccessibility="no".

Consultez ces principes et ce parcours de formation pour savoir comment rendre votre application plus accessible.

(Facultatif) Ajuster la teinte des drawables vectoriels

Dans cette tâche facultative, vous allez attribuer une teinte aux icônes en fonction de la couleur principale du thème, de sorte qu'elles s'affichent différemment dans les thèmes clair et sombre (comme illustré ci-dessous). Ce changement complémente l'interface utilisateur pour rendre les icônes plus cohérentes avec le thème de l'application.

77092f702beb1cfb.png 80a390087905eb29.png

Comme nous l'avons mentionné précédemment, l'un des avantages des images VectorDrawables par rapport aux images bitmap est la possibilité de les redimensionner et de les colorer. Le code XML ci-dessous représente l'icône en forme de cloche. Deux attributs de couleur sont à noter : android:tint et android:fillColor.

ic_service.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
   android:width="24dp"
   android:height="24dp"
   android:viewportWidth="24"
   android:viewportHeight="24"
   android:tint="?attr/colorControlNormal">
 <path
     android:fillColor="@android:color/white"
     android:pathData="M2,17h20v2L2,19zM13.84,7.79c0.1,-0.24 0.16,-0.51 0.16,-0.79 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2c0,0.28 0.06,0.55 0.16,0.79C6.25,8.6 3.27,11.93 3,16h18c-0.27,-4.07 -3.25,-7.4 -7.16,-8.21z"/>
</vector>

aab70a5d4eaabdc7.png

Lorsqu'une teinte est spécifiée, elle remplace les éventuelles instructions fillColor pour le drawable. Dans ce cas, la couleur blanche est remplacée par l'attribut de thème colorControlNormal. colorControlNormal est la couleur de l'état "normal" (non sélectionné/désactivé) d'un widget. Actuellement, il s'agit d'une couleur grise.

Nous pouvons apporter une amélioration visuelle à l'application en colorant le drawable en fonction de la couleur principale du thème de l'application. Pour le thème clair, l'icône apparaît en @color/green, et pour le thème sombre, elle apparaît en @color/green_light, ce qui correspond à notre ?attr/colorPrimary. Colorer les drawables en fonction de la couleur principale du thème de l'application permet de rendre ces éléments de mise en page plus cohérents et harmonieux. Cela évite également de devoir dupliquer les icônes pour gérer le thème clair et le thème sombre. Vous n'utilisez qu'un seul jeu de drawables vectoriels, dont la teinte change en fonction de l'attribut de thème colorPrimary.

  1. Modifiez la valeur de l'attribut android:tint dans ic_service.xml.
android:tint="?attr/colorPrimary"

Dans Android Studio, cette icône a maintenant la bonne teinte.

f0b8f59dbf00a20b.png

La valeur à laquelle fait référence l'attribut du thème colorPrimary diffère entre le thème clair et le thème sombre.

  1. Répétez la même procédure pour modifier la teinte des autres drawables vectoriels.

ic_store.xml

<vector ...
   android:tint="?attr/colorPrimary">
   ...
</vector>

ic_round_up.xml

<vector ...
   android:tint="?attr/colorPrimary">
   ...
</vector>
  1. Exécutez l'application. Vérifiez que les icônes s'affichent différemment dans les thèmes clair et sombre.
  2. Pour finir, n'oubliez pas de reformater tous les fichiers de code XML et Kotlin de votre application.

Félicitations ! Vous avez terminé l'application de calcul des pourboires. Vous pouvez être fier de votre création. Nous espérons que cet atelier vous aidera à créer des applications encore plus belles et fonctionnelles.

7. Code de solution

Le code de solution de cet atelier de programmation figure dans le dépôt GitHub indiqué ci-dessous.

5743ac5ee2493d7.png ab4acfeed8390465.png

Pour obtenir le code de cet atelier de programmation et l'ouvrir dans Android Studio, procédez comme suit :

Obtenir le code

  1. Cliquez sur l'URL indiquée. La page GitHub du projet s'ouvre dans un navigateur.
  2. Vérifiez que le nom de la branche correspond au nom spécifié dans l'atelier de programmation. Par exemple, dans la capture d'écran suivante, le nom de la branche est main.

8cf29fa81a862adb.png

  1. Sur la page GitHub du projet, cliquez sur le bouton Code pour afficher une fenêtre pop-up.

1debcf330fd04c7b.png

  1. 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.
  2. Recherchez le fichier sur votre ordinateur (il se trouve probablement dans le dossier Téléchargements).
  3. 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

  1. Lancez Android Studio.
  2. Dans la fenêtre Welcome to Android Studio (Bienvenue dans Android Studio), cliquez sur Open (Ouvrir).

d8e9dbdeafe9038a.png

Remarque : Si Android Studio est déjà ouvert, sélectionnez l'option de menu File > Open (Fichier > Ouvrir).

8d1fda7396afe8e5.png

  1. 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).
  2. Double-cliquez sur le dossier de ce projet.
  3. Attendez qu'Android Studio ouvre le projet.
  4. Cliquez sur le bouton Run (Exécuter) 8de56cba7583251f.png pour créer et exécuter l'application. Assurez-vous qu'elle fonctionne correctement.

8. Résumé

  • Dans la mesure du possible, utilisez des composants Material pour respecter les consignes Material Design et permettre une personnalisation plus poussée.
  • Ajoutez des icônes pour fournir aux utilisateurs des indicateurs visuels sur le fonctionnement des différentes parties de votre application.
  • Utilisez ConstraintLayout pour positionner des éléments dans votre mise en page.
  • Testez votre application pour identifier les cas atypiques (par exemple, pour faire pivoter l'écran en mode Paysage) et apportez les améliorations nécessaires, le cas échéant.
  • Commentez votre code pour aider les personnes qui le liront à comprendre votre approche.
  • Reformatez votre code et nettoyez-le pour éliminer tout ce qui est superflu.

9. En savoir plus

10. Pour s'entraîner

  • Dans la continuité des ateliers de programmation précédents, mettez à jour votre application de conversion des unités de mesure pour les recettes pour l'aligner sur les consignes Material Design en appliquant les bonnes pratiques que vous avez apprises (comme l'utilisation des composants Material Design).