0% ont trouvé ce document utile (0 vote)
81 vues109 pages

Projet Android

Le document explique les concepts fondamentaux de la programmation mobile Android, en se concentrant sur les briques graphiques essentielles telles que les Activités et les Fragments. Il guide également l'utilisateur à travers le processus de création d'un projet Android dans Android Studio, en utilisant un modèle prédéfini et en configurant divers paramètres. Enfin, il présente la structure d'un projet Android, y compris les répertoires principaux et le rôle de la classe MainActivity.

Transféré par

kengneelsa28
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
81 vues109 pages

Projet Android

Le document explique les concepts fondamentaux de la programmation mobile Android, en se concentrant sur les briques graphiques essentielles telles que les Activités et les Fragments. Il guide également l'utilisateur à travers le processus de création d'un projet Android dans Android Studio, en utilisant un modèle prédéfini et en configurant divers paramètres. Enfin, il présente la structure d'un projet Android, y compris les répertoires principaux et le rôle de la classe MainActivity.

Transféré par

kengneelsa28
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Programmation Mobile Android MOUOMENE

I-Créez le projet et prenez en main l’environnement


Découvrez les briques graphiques fondamentales
Avant de créer votre premier projet Android, il est nécessaire de définir les deux
briques graphiques fondamentales d’une application Android : Activité et Fragment.

Activité
Une activité (ou Activity, en anglais) est le point d’entrée de n’importe quelle
application Android. C'est elle qui va contenir l'ensemble des éléments graphiques du
type texte, bouton, titre, etc. Elle intercepte aussi les interactions faites à l’écran par
l’utilisateur.

Fragment
Il existe une autre brique graphique fondamentale, qui s’appelle Fragment. Un
fragment contient également des éléments visuels. Il correspond à une portion d’un
écran, voire à un écran complet. Tout comme une activité, il est également capable
d’intercepter les interactions de l’utilisateur. Un fragment ne peut exister qu’au sein
d’une activité.

Une bonne pratique sur Android consiste à avoir une activité unique qui correspond à
l’écran complet, à laquelle sont ajoutés différents fragments.

Exemple de décomposition d’une application contenant une activité unique et


plusieurs fragments
Dans l’illustration ci-dessus, nous avons par exemple une application qui comporte
une activité unique. Cette activité est composée d’un menu, et d’un contenu central
dans lequel seront ajoutés différents fragments en fonction du menu sélectionné. Par
exemple, si c’est le menu “Messages” qui est sélectionné, alors c’est le fragment
correspondant à l’interface de la messagerie qui sera ajouté. Si c’est le menu
“Accueil” qui est sélectionné, c’est le fragment correspondant à l’interface d’accueil
qui sera ajouté, etc.

Créez le projet

P a g e 1 | 109
Programmation Mobile Android MOUOMENE

Cliquez sur le bouton New Project


Pour vous faciliter le travail, l'assistant de création d’Android Studio permet de créer
automatiquement des applications avec un squelette prédéfini. Par exemple, une
application affichant une carte Google Map, ou une application avec un menu
principal en bas de l’écran.

Tous ces templates d’application comportent dans leur nom le terme Activity, le
fameux point d’entrée d’une application Android.
Pour développer notre application SuperQuiz, nous allons choisir le template Empty
Views Activity. Celui-ci se chargera de créer une activité principale. Cliquez
sur Next.

P a g e 2 | 109
Programmation Mobile Android MOUOMENE

Choisissez le template Empty View Activity.


Configurez le projet
Dans le champ Name, vous allez saisir le nom de l'application. Par défaut, ce sera le
nom qui apparaîtra en dessous de l'icône de l'application sur l'écran d'accueil du
téléphone, et dans la barre de titre de l'application. Il vous sera tout à fait possible de
le modifier par la suite. Saisissez “SuperQuiz” (ou tout autre nom que vous trouvez
meilleur).

Le champ Package name permet de déterminer quel nom de package utiliser pour
votre application. Cela permet par la suite de distinguer votre application d'une autre
application qui porterait le même nom. Par convention, la notation inverse est
utilisée. Par exemple, si vous travaillez dans la société WorldCompany, vous
pourriez préciser “com.worldcompany.android”. Après, vous êtes libre de préciser le
nom de votre choix. Évitez simplement d'utiliser un nom de domaine qui ne vous
appartient pas, afin de ne pas être confronté à un doublon le jour où vous souhaitez
publier votre application sur le Google Play Store.

Pour en savoir plus sur les conventions de nommage Java, c'est par là.
Modifiez éventuellement le chemin du projet dans Save location,
utilisez Java comme langage et sélectionnez Kotlin DSL comme Build configuration
language.

P a g e 3 | 109
Programmation Mobile Android MOUOMENE

Configurez votre projet en saisissant le nom de l'application, le nom de package, le


chemin du projet et le langage.
Vous devez également préciser la version minimale de SDK à utiliser pour votre
projet, dans le champ Minimum SDK. Chaque version du SDK correspond à une
version du système Android. On l’appelle également API Level. Utiliser une API
élevée (donc récente) vous permet de bénéficier des dernières fonctionnalités
proposées par Android. Toutefois, les anciens appareils présents sur le marché et
qui ne sont pas à jour ne pourront pas faire fonctionner votre application. Pour vous
aider dans votre choix, cliquez sur le lien Help me choose. Vous verrez apparaître
un écran semblable à celui-ci :

P a g e 4 | 109
Programmation Mobile Android MOUOMENE

Cliquez sur Help me choose pour vérifier dans quelle mesure votre application pourra
être utilisée par des appareils.
Ce graphique vous permet de vérifier le pourcentage d'appareils qui seront capables
d'installer et de lancer votre application. À ce jour, en choisissant l'API
24 correspondant à la version de Android 7.0 (Nougat), Android Studio précise que
l'application sera en mesure de fonctionner sur plus de 94 % des appareils. C’est un
bon compromis qui permet de bénéficier de la plupart des nouveautés du SDK, et en
même temps de lutter contre l'obsolescence logicielle !

N'hésitez pas à consulter la page Wikipedia sur l'historique des versions d'Android
(en français ou en anglais).
Laissez décochée la case Use legacy android.support libraries.

Cliquez sur le bouton Finish. Vous verrez apparaître alors l'écran principal d'Android
Studio ! Patientez encore un peu, car à ce stade, les fichiers nécessaires au bon
fonctionnement de votre projet sont en train d’être téléchargés. Cela devrait prendre
un peu moins d’une minute.
Créez le fragment d’accueil et liez-le à l’activité
Nous allons maintenant créer le fragment qui correspondra à l’écran d’accueil de
notre application. Pour cela, dans le menu Files d'Android Studio, allez dans New >
Fragment > Fragment (Blank).

P a g e 5 | 109
Programmation Mobile Android MOUOMENE

Ajouter un nouveau fragment vide.


Si vous ne voyez pas le fragment s'afficher dans le menu, il faudra l'implémenter.
Pour ce faire, dans votre dossier Gradle Scripts, ouvrez build.gradle et ajouter
dans les dependencies la ligne suivante
: implementation("androidx.fragment:fragment:1.6.1") .
Ensuite, nommez ce fragment WelcomeFragment . Vous verrez que le champ
“Fragment Layout Name” se complète alors tout seul, avec le nom
“fragment_welcome”. Ce nommage nous convient parfaitement. Cliquez sur Finish .

P a g e 6 | 109
Programmation Mobile Android MOUOMENE

Nommer le nouveau fragment WelcomeFragment.


Petite parenthèse concernant le nommage des fichiers au sein d’un projet Android.
Par convention, les classes de type Activité ou Fragment sont suffixés
respectivement par Activity ou Fragment , en utilisant la notation
appelée PascalCase. Cela donne donc MainActivity ou WelcomeFragment .
Concernant le fichier XML de type layout associé, il est suffixé respectivement
par activity ou fragment , en utilisant la notation snake_case. Ainsi le fichier
layout associé au fragment WelcomeFragment est nommé fragment_welcome.xml .

Découvrez la vue projet d'Android Studio


L'écran principal
D'une façon tout à fait classique, vous avez sur le côté gauche l'arborescence des
fichiers, et au centre de l’écran, le contenu du fichier en cours d'édition.

Vous constaterez qu'en plus des traditionnels boutons et menus en haut de l'écran,
plusieurs menus ornent les bords de la fenêtre principale. Eh oui, c'est une
fonctionnalité assez originale d'Android Studio !

P a g e 7 | 109
Programmation Mobile Android MOUOMENE

Découvrez l'interface d'Android Studio


Pour l'instant, le bouton qui vous intéresse est le 2 sur la capture d'écran ci-dessus :
il permet d'afficher l'arborescence des fichiers du projet. Ensuite, chaque fichier
ouvert s'affiche dans un onglet (3 sur la capture d'écran ci-dessus).

Si vous ne voyez plus aucun bouton sur les bords, ne paniquez pas. Il vous suffit de
cliquer sur le petit carré en bas à gauche de la fenêtre (1 sur la capture d'écran ci-
dessus).
Regardez maintenant tout en bas de l’IDE. Vous voyez un menu constitué de
plusieurs boutons. Ils permettent de définir le contenu qui s'affiche dans la partie
basse de la fenêtre d'Android Studio.

Le menu du bas

P a g e 8 | 109
Programmation Mobile Android MOUOMENE

Dans la capture précédente, c’est le menu Terminal qui est sélectionné, permettant
ainsi d’avoir un terminal en bas de l’écran. C’est très pratique si vous êtes à l'aise
avec les lignes de commande. Cela vous évite de devoir changer de fenêtre. Les
autres options très utiles dans ce menu sont :

 Git : pour accéder à une interface facilitant la gestion du code de l’application,


si celui-ci est associé à un Repository Git ;
 Logcat : permet d’afficher en direct les logs de l’application lorsqu’elle est
déployée sur un appareil. Si votre application plante, c’est ici que vous
trouverez les détails du crash ;
 Build : pour suivre les étapes qui se déroulent lors de la compilation de votre
code.
Mais au fait, c’est quoi un log ? Tout au long de son exécution, une application
enregistre ce qu’il se passe d’un point de vue technique dans un journal de bord,
appelé aussi log. Ces informations peuvent être riches en enseignements, en
particulier pour analyser les erreurs qui peuvent survenir.
L'arborescence de fichiers
L'affichage des fichiers de votre projet doit ressembler à celui-ci :

L'affichage des fichiers de votre projet

P a g e 9 | 109
Programmation Mobile Android MOUOMENE

Comme entouré sur la capture précédente, vous pouvez voir en haut à droite de
cette fenêtre intégrée un menu déroulant, avec l’option Android sélectionnée par
défaut. Ce menu permet de personnaliser le mode d’affichage des fichiers visibles
dans l’arborescence. Quand l’option Android est sélectionnée, seuls les fichiers
spécifiques au développement Android sont visibles. Vous ne voyez donc pas
certains fichiers présents dans le répertoire du projet, tels que le
fichier .gitignore de votre projet ou encore le fichier README.md . Pour visualiser
tous les fichiers de votre projet, vous pouvez sélectionner dans ce menu
l’option Project.

Sélectionnez l’option Project pour visualiser tous les fichiers présents dans le
répertoire de votre projet

P a g e 10 | 109
Programmation Mobile Android MOUOMENE

Les répertoires principaux


Revenez maintenant à l'arborescence des fichiers de votre projet. Vous devez
distinguer les trois répertoires principaux d'un projet Android, qui
sont manifests , java et res . Découvrons à quoi servent ces différents répertoires.
Le répertoire manifests
Ce répertoire contient généralement un seul fichier : le fichier AndroidManifest.xml .
Ce fichier est la carte d'identité de votre application. Il permet entre autres de
préciser le nom de l'application, l'icône à utiliser, quelle activité lancer au démarrage,
etc.
Le répertoire java
Ce répertoire contient l'ensemble du code source Java ou Kotlin de l'application,
ainsi que les différents tests associés. Dans notre exemple, nous voyons apparaître
entre autres le fichier MainActivity (l'extension .java est automatiquement
masquée par l'IDE). Au fur et à mesure de l'avancement du projet, ce répertoire se
remplira de fichiers, voire de sous-dossiers afin d'isoler les composants fonctionnels
entre eux.
Kotlin ? Qu’est-ce que c’est ?
Java n'est pas le seul langage de développement disponible pour développer des
applications Android. Google a annoncé en 2018 que le langage Kotlin était
désormais le langage officiel pour Android. Si vous êtes curieux, n'hésitez pas à
aller voir par ici.
Le répertoire res
Ce répertoire contient toutes les ressources de l'application, et comprend quatre
sous-répertoires :

 le dossier drawable , qui contient l'ensemble des images et contenus à


afficher à l'écran (par exemple une image, une icône ou un logo) ;
 le dossier layout , contenant l'ensemble des fichiers qui décrivent le rendu
visuel des différents écrans de l’application ;
 le dossier mipmap , qui contient principalement l'icône de lancement de
l'application ;
 le dossier values , qui contient différents paramétrages et valeurs, par
exemple les couleurs à utiliser dans l'application, les différentes traductions de
texte à utiliser, ou les styles graphiques à appliquer.

En résumé
 Pour créer un projet sur Android Studio, vous devez le configurer en utilisant
l’un des templates prédéfinis. Ces templates vont créer automatiquement
l’activité principale, c’est-à-dire la brique fondamentale dans l’interaction avec
l’utilisateur, et d'autres fichiers si nécessaire.
 Un projet Android est composé de trois répertoires principaux : manifests, java
et res.

P a g e 11 | 109
Programmation Mobile Android MOUOMENE

II-Prenez en main le projet sur Android Studio


Creusons maintenant un peu plus le contenu de notre projet, en ouvrant les fichiers
principaux.
MainActivity.java
Dans l’arborescence des fichiers, cliquez sur le fichier MainActivity.java , présent
au sein du répertoire java .

Arborescence initiale des fichiers


Pour rappel, une activité, ou Activity, en anglais, est une brique fondamentale
d'Android. C'est le point d'entrée de n'importe quelle application Android.

Une activité a pour rôle principal d'interagir avec l'utilisateur. C'est une classe Java
ou Kotlin, qui hérite obligatoirement de la classe
Android Activity ou AppCompatActivity .
Par définition, une activité Android hérite toujours (plus ou moins directement) de la
classe Activity . Sur les versions d'Android un peu plus anciennes, certaines
fonctionnalités récentes ne sont pas officiellement supportées. De ce fait, hériter
d'AppCompatActivity permet de corriger ce problème en "émulant" ces nouvelles
fonctionnalités.
Analysons le code contenu dans cette classe MainActivity.java . Sans même que
vous ayez encore écrit une seule ligne de code, voici l’implémentation que vous
devriez retrouver :
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

P a g e 12 | 109
Programmation Mobile Android MOUOMENE

}
Dans ce code, nous remarquons principalement qu’une fonction onCreate est
surchargée par défaut. Cette méthode est en fait appelée par le système lorsque
l'activité est créée.
La ligne qui nous intéresse ici est la ligne qui contient l’appel à la
méthode setContentView() . Elle permet de déclarer la vue associée à cette activité.
C’est grâce à elle que nous allons pouvoir indiquer au système où se trouve le fichier
de ressource qui est chargé de décrire les éléments graphiques contenus dans cette
vue.
Mais de quel fichier parle-t-on ? La classe MainActivity.java ne contient-elle pas la
description de l’interface ?
En réalité, ce qu’il faut savoir, c’est que toute Activité ou tout Fragment se compose
de deux fichiers :

 une classe Java ou Kotlin qui hérite de la classe


Android Activity ou Fragment , et qui contient la logique spécifique à cette
interface ;
 un fichier XML appelé layout , qui contient la description des éléments
graphiques contenus dans cette interface.

Déclaration d’une activité en deux fichiers


Au sein de la classe MainActivity , la ligne de code
suivante, setContentView(R.layout.activity_main); , permet donc de lier ces
deux fichiers. C’est en quelque sorte le + représenté sur le schéma ci-dessus. Le
fichier XML est spécifié dans cette fonction avec une syntaxe particulière
: R.layout.nom_du_fichier (sans l'extension XML).
Lors de la compilation du projet, Android Studio génère une classe Java appelée R
(pour Resources), qui contient l'ensemble des identifiants de toutes les ressources
du projet. Ces ressources peuvent être des fichiers layout, des chaînes de
caractères, etc. Nous verrons à quoi ressemble cette classe après avoir compilé le
projet.
P a g e 13 | 109
Programmation Mobile Android MOUOMENE

Petite astuce : vous pouvez naviguer facilement dans le code source en utilisant la
souris et en cliquant sur une méthode, une classe ou un paramètre. Pour ce faire,
positionnez le curseur sur le mot-clé qui vous intéresse, maintenez la
touche ⌘ enfoncée sur Mac, ou la touche CTRL sous Windows, et faites un clic
gauche. Essayez dans le fichier MainActivity.java en cliquant
sur AppCompatActivity , setContentView ou activity_main !
activity_main.xml
Jetons maintenant un coup d'œil au layout activity_main.xml associé à l’activité
principale de notre application. Sélectionnez “Split” en haut à droite de l’éditeur pour
visualiser en parallèle une prévisualisation du layout et le code XML correspondant.

Cliquez sur Split. Visualisez le code à gauche et la prévisualisation correspondante à


droite
Il contient le code XML suivant :

<?xml version="1.0" encoding="utf-8"?>


<androidx.constraintlayout.widget.ConstraintLayout
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_width="match_parent"
android:layout_height="match_parent"

P a g e 14 | 109
Programmation Mobile Android MOUOMENE

tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
À ce stade, vous pouvez simplement constater que ce code permet d’afficher un
texte “Hello World!” grâce au composant TextView . Le texte est spécifié grâce à sa
propriété android:text .
Amusez-vous à remplacer le texte par défaut. Nous observerons le résultat dans
quelques minutes.
WelcomeFragment.java
Attardons-nous brièvement sur les
fichiers WelcomeFragment.java et fragment_welcome.xml . De la même manière que
pour l’activité principale du projet, ce fragment se décompose en deux fichiers.
L’implémentation par défaut du fragment créé via le template “Fragment (Blank)”
varie selon la version d’Android Studio que vous utilisez, et contient souvent
beaucoup de code inutile initialement. Faites donc en sorte que votre
fichier WelcomeFragment.java contienne le code ci-dessous :
public class WelcomeFragment extends Fragment {
public static WelcomeFragment newInstance() {
WelcomeFragment fragment = new WelcomeFragment();
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_welcome, container, false);
}
}
Dans cette implémentation, vous pouvez remarquer en particulier les éléments
suivants :

 la classe WelcomeFragment étend la classe Android Fragment ;


 les fonctions onCreate et onCreateView , issues de la classe Fragment, sont
surchargées. Nous verrons dans le prochain chapitre quel est leur but précis ;

P a g e 15 | 109
Programmation Mobile Android MOUOMENE

 enfin, la partie la plus intéressante de ce fichier pour le moment se situe au


niveau de la fonction onCreateView . Elle retourne un objet de type View , qui
est obtenu grâce à la fonction inflate() . Cette fonction prend en particulier
en paramètre la ressource R.layout.fragment_welcome , qui désigne le
fichier fragment_welcome.xml du projet. Vous l’avez compris, c’est donc ici
que le lien entre la classe Java et le fichier XML contenant la description de la
vue du fragment est fait.

Déclaration d’un fragment en deux fichiers


fragment_welcome.xml
Ouvrez le fichier fragment_welcome.xml en double-cliquant dessus.
Votre fichier fragment_welcome.xml contient la description d’un écran par défaut,
pouvant varier selon votre IDE. Pour le moment ça n’est pas très grave. Dans mon
cas, il contient :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WelcomeFragment">

<!-- TODO: Update blank fragment layout -->


<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />

</FrameLayout>
À ce stade du cours, ne nous attardons pas dans le détail sur tous les paramètres de
chaque balise. Portez simplement votre attention sur les point suivants :

 le composant FrameLayout enveloppe un autre composant TextView ;


P a g e 16 | 109
Programmation Mobile Android MOUOMENE

 vous verrez dans la prochaine partie que FrameLayout est un composant de


type conteneur, qui permet de superposer des éléments ;
 le composant TextView permet d’afficher un texte.
En résumé
 Une activité ou un fragment est constitué de 2 fichiers : une classe Java et un
fichier XML.
 La classe Java appelée R permet de référencer les ressources depuis le
code Java.
 Le lien entre une classe de type Activity et son XML est effectué via la
fonction setContentView() . Pour un fragment, c’est au cœur de la
fonction onCreateView() que la liaison est faite.

III-Démarrez l’application
Grâce au template Android Studio, nous avons une application Android d’ores et déjà
fonctionnelle. Concrètement, elle se compose actuellement d’une activité qui contient
un texte que vous avez personnalisé dans le chapitre précédent.

Comment faire maintenant pour observer le rendu final de cette application sur un
téléphone ?
C’est ce que nous allons découvrir tout de suite. Ce chapitre vous montre
comment lancer votre application. Vous avez le choix : soit sur un émulateur,
c'est-à-dire un appareil virtuel qui se lance sur votre ordinateur, soit sur
votre téléphone (ou tablette).

Utiliser l'émulateur offre l'avantage de générer facilement différentes configurations,


avec des tailles d'écran différentes, une mémoire vive limitée ou une ancienne
version d'Android, par exemple. Après, rien ne vaut un test sur un équipement réel,
afin de s'assurer que l'expérience utilisateur soit la meilleure possible.

Lancez l'émulateur
Pour lancer l'application, il suffit de cliquer sur le bouton de lecture vert situé dans la
barre de l'IDE :

Lancez l'émulateur
Oups, la notification suivante s'affiche sûrement en bas de votre fenêtre :

P a g e 17 | 109
Programmation Mobile Android MOUOMENE

Une notification s’affiche lorsque vous tentez de lancer l'émulateur


Créez un équipement virtuel
Eh oui, afin de pouvoir lancer l'application sur un émulateur, il est nécessaire de
créer un équipement virtuel au préalable. Un équipement virtuel correspond à
l'association d'un type d'équipement (la partie matérielle) avec une version d'Android
(la partie logicielle). Pour cela, cliquez sur le bouton Device Manager, ou sur le
menu “Device Manager” sur le bord droit de la fenêtre de votre IDE.

Cliquez sur l'icône correspondant au Device Manager pour créer un équipement


virtuel
Une nouvelle fenêtre intégrée à Android Studio s’ouvre alors sur la droite de votre
écran.

Fenêtre du Device Manager intégré à droite dans Android Studio

P a g e 18 | 109
Programmation Mobile Android MOUOMENE

Comme son nom l’indique, le Device Manager vous permet de gérer vos émulateurs.
Pour le moment, la liste est vide… On va y remédier ! Cliquez sur le bouton Create
Device (dans l’onglet Virtual de cette fenêtre). La fenêtre suivante apparaît alors.

Type d’équipement

Sélectionnez le type d'équipement sur lequel vous allez tester votre application
Dans la partie gauche de la fenêtre, vous pouvez sélectionner le type d'équipement.
Dans notre cas, ce sera Phone, mais à l'avenir vous pourrez choisir Tablet si vous
souhaitez tester l'application sur une tablette, par exemple.

La partie du milieu liste l'ensemble des équipements disponibles. Chaque


équipement possède des caractéristiques matérielles spécifiques (taille de l'écran,
résolution, mémoire disponible, etc.). Si un jour vous souhaitez émuler un
équipement très spécifique (avec un écran très grand ou peu de mémoire), vous
pourrez le créer en cliquant sur le bouton New Hardware Profile . Pour notre besoin,
vous pouvez sélectionner le Pixel 4, qui fera parfaitement l'affaire. Cliquez ensuite
sur le bouton Next .

P a g e 19 | 109
Programmation Mobile Android MOUOMENE

Sélectionnez les caractéristiques matérielles de votre équipement : Pixel 4, et cliquez


sur Next
Image système

L'image système correspond à la version d'Android à installer sur l'équipement


virtuel. Je vous conseille de rester dans l'onglet “Recommended” dans un premier
temps. Il est possible que vous deviez installer une image système pour pouvoir aller
plus loin : cliquez pour cela sur le bouton sous forme d’icône situé à côté de l'image
que vous souhaitez installer.

Cliquez sur l'icône de téléchargement pour installer une image système


La fenêtre SDK Component Installer s'affiche ensuite, le temps de télécharger les
fichiers nécessaires. Les fichiers étant assez volumineux, vous avez sûrement le
temps de vous faire un café. Ou un thé. Ou une tisane. À la fin de l'installation,
cliquez sur le bouton Finish.

P a g e 20 | 109
Programmation Mobile Android MOUOMENE

Cliquez sur l'icône de téléchargement pour installer une image système


De retour sur l'écran System Image, cliquez sur le bouton Next pour arriver sur
l'écran Android Virtual Device (AVD). Cet écran vous permet de renommer votre
appareil et de vérifier la configuration de votre appareil virtuel, voire d'en changer
certains paramètres. Pour l'instant, je vous conseille de conserver les paramètres par
défaut. Cliquez sur Finish.

Cliquez sur le bouton Finish, pour finaliser la création de votre émulateur


P a g e 21 | 109
Programmation Mobile Android MOUOMENE

Lancement
Vous devez maintenant voir apparaître votre nouvel équipement virtuel dans la
fenêtre du Device Manager. Super ! Vous pouvez fermer la fenêtre. L’émulateur
est sélectionné automatiquement, vous pouvez donc maintenant lancer
l’application !

1 : L'émulateur est sélectionné automatiquement, 2 : Vous pouvez lancer l'application


Si vous êtes sous Windows avec un processeur AMD, il se peut que vous rencontriez
un blocage lors de la virtualisation de l’émulateur. Pour régler cela, il vous faut activer
le paramètre de virtualisation via le menu BIOS de votre ordinateur. Plus
d'informations sur la documentation officielle.
Sélectionnez l'équipement puis cliquez sur OK. Normalement, l'émulateur devrait se
lancer dans une n-ième fenêtre intégrée à Android Studio, et après le chargement
d'Android, vous devriez voir apparaître votre application, avec le texte que vous avez
saisi dans le composant TextView de votre Fragment. Je vous laisse découvrir le
mien :

L'application est lancée sur l'émulateur !


P a g e 22 | 109
Programmation Mobile Android MOUOMENE

La barre d'outils en haut de la fenêtre qui contient l'émulateur vous permet de


modifier certains paramètres. Ils sont tous assez explicites. Soyez curieux ou
curieuse, testez chacun d'eux ! Vous pouvez même cliquer sur les trois petits points
en bas pour afficher davantage de paramètres.

Testez sur équipement réel


Rien ne vaut un test sur un équipement réel ! Si vous avez la chance d'avoir un
équipement Android sous le coude (téléphone ou tablette), c'est le moment de
dégainer son câble USB et de le brancher à votre ordinateur. (On peut également
appareiller un téléphone via le WiFi, vous pouvez essayer mais chez moi, ça ne
fonctionne pas très bien.)

Activation du mode développeur


Afin de permettre à Android Studio de communiquer avec votre équipement, il est
nécessaire d'activer le mode développeur. Pour ce faire, suivez les instructions de la
section Enable Developer Options détaillées sur cette page.

Ensuite, vous devez permettre à votre système d'exploitation de détecter


l'équipement. Si vous êtes sur Mac, profitez car vous n'avez rien à faire ! Si vous êtes
sur Windows ou Linux, suivez les instructions détaillées dans la section Set up a
device for development de cette page.

Lancement
Si l'application est déjà lancée sur l'émulateur, arrêtez-la en cliquant sur le
bouton Stop (carré rouge situé sur la barre d'outils d'Android Studio).

Arrêtez l'application en cliquant sur le bouton Stop


Ensuite, cliquez sur le menu déroulant portant le nom de votre émulateur (pour ma
part, c’est Pixel 4 API 33). Vous devriez voir apparaître la fenêtre suivante, qui vous
permet de choisir entre un lancement de l'application sur l'émulateur ou sur votre
équipement réel (sur la capture d’écran, OnePlus KB2003). Sélectionnez votre
équipement puis cliquez sur le bouton Play !

P a g e 23 | 109
Programmation Mobile Android MOUOMENE

Cliquez sur le menu déroulant pour sélectionner le mode de lancement de votre


application

En résumé
 Il est possible de lancer l’application sur un émulateur ou sur un téléphone
physique.
 L’émulateur vous permettra de générer facilement votre application avec
différentes configurations.
 Sur un équipement réel, vous pouvez vous assurer que l’expérience utilisateur
est conforme avec ce que vous avez envisagé.

IV Faites vos premiers pas dans la conception


d'interface sous Android Studio
Dans cette partie, vous allez dessiner l'interface utilisateur du premier écran de
l'application. Pour ce premier écran, nous souhaitons accueillir l'utilisateur en lui
demandant de saisir son nom. De fait, cet écran va être composé de textes, d'une
zone de saisie et d'un bouton. Le résultat attendu est le suivant :

Votre écran d'accueil (en anglais) composé d'un champ texte, d'une zone de saisie et
d'un bouton

P a g e 24 | 109
Programmation Mobile Android MOUOMENE

Concevez le squelette de l’application


Avant de se lancer tête baissée dans le code, il est important de se demander
comment concevoir le squelette de votre application correctement.

Une activité unique et plusieurs fragments


Pour rappel, une bonne pratique sur Android consiste à avoir une activité
unique qui est le point d’entrée de l’application, et qui visuellement occupe tout
l’écran de l’appareil. À cette activité sont ajoutés à tour de rôle des fragments.
Un fragment occupe soit tout l’écran de l’application, soit une portion de l’écran.

Cette pratique, apparue en 2018, est venu simplifier le développement Android, en


particulier en facilitant :
 la navigation d’un écran à un autre ;
 le partage de données entre plusieurs écrans, en utilisant notamment une
classe de type ViewModel (nous définirons en détail plus tard comment
fonctionne ce type de classe) ;
 la possibilité d’adapter l’espace occupé par un fragment selon la taille d’un
appareil, comme illustré ci-dessous.

Deux fragments au sein de la même activité sur un téléphone aux dimensions


standard, qui s’affichent l’un après l’autre, suite à une action de l’utilisateur

P a g e 25 | 109
Programmation Mobile Android MOUOMENE

Deux fragments au sein de la même activité, qui s’adaptent à la taille de l’écran et


s’affichent côte à côte sur une tablette.
Découpage en fragments de l’application SuperQuiz
Nous allons donc appliquer cette bonne pratique dans le développement de notre
application SuperQuiz. Pour cela, nous utiliserons une activité principale
appeléeMainActivity , qui contiendra à tour de rôle les fragments suivants :
 au démarrage de l’application, elle affiche un premier
fragment, WelcomeFragment , qui correspond à l’écran permettant à l’utilisateur
de saisir son nom ;
 lorsque l’utilisateur appuie sur le bouton “Let’s play”, le
fragment WelcomeFragment est remplacé par le fragment QuizFragment ;
 le fragment QuizFragment est chargé d’afficher une à une les questions du
quiz.

P a g e 26 | 109
Programmation Mobile Android MOUOMENE

Maquette de l’application SuperQuiz illustrant le découpage en fragments


D’accord, mais actuellement dans mon projet l’activité MainActivity ne contient pas
encore de fragment, mais un texte que j’ai personnalisé dans la première partie. 🤔
Effectivement. C’est pourquoi la première étape va consister à inclure l’écran
correspondant au fragment WelcomeFragment dans l’activité principale de notre
application.
Inclure WelcomeFragment dans MainActivity
Pour cela, nous allons modifier le code du layout activity_main.xml , pour y
positionner un composant de type FragmentContainerView . Comme son nom
l’indique, il permet de contenir un fragment.
Voici le code permettant d’insérer ce composant dans notre layout :
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.mycompany.superquiz.WelcomeFragment" />
Faites bien attention à compléter correctement la propriété android:name avec le
nom du package créé tout au début du projet, qui permet de spécifier le fragment
initial à démarrer, en particulier avec le chemin menant à ce fichier.

P a g e 27 | 109
Programmation Mobile Android MOUOMENE

Maintenant, si vous lancez à nouveau votre application sur un appareil, vous verrez
qu’elle affiche l’écran correspondant au fragment Welcome .
Découvrez l’éditeur graphique
Nous sommes maintenant prêts à concevoir l’interface de notre premier écran. Dans
un premier temps, cela se passe dans l’éditeur graphique d'Android Studio. Prenons
le temps de le découvrir ensemble.

Sous Android Studio, naviguez dans l'arborescence du projet, et ouvrez le fichier


fraîchement renommé en fragment_welcome.xml situé dans res/layout, en double-
cliquant dessus.
Par défaut, Android Studio ouvre l'éditeur en mode Design, c'est-à-dire que vous
pouvez placer et configurer les différents éléments graphiques avec votre souris, et
générer automatiquement le contenu XML.

Vue de l’éditeur du layout “fragment_welcome.xml” en mode Design


Cela semble séduisant, mais nous allons plutôt utiliser le mode Split, permettant
d'avoir une meilleure maîtrise de l'ensemble. Pour ce faire, cliquez sur
l'onglet Split en haut à droite de l'écran. Vous verrez apparaître le contenu XML sur
la droite. L’onglet Code permet quant à lui de visualiser uniquement le code XML.

P a g e 28 | 109
Programmation Mobile Android MOUOMENE

Cliquez sur Split en haut à droite de l’éditeur de layout dans Android Studio
Le contenu actuel de ce fichier est :

<?xml version="1.0" encoding="utf-8"?>


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WelcomeFragment">

<!-- TODO: Update blank fragment layout -->


<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />

</FrameLayout>
En mode Split , vous pouvez voir le résultat en temps réel sur la partie droite de la
fenêtre.
En résumé
 Une activité unique est utilisée comme point d'entrée de l'application.
 Des fragments sont ajoutés à cette activité pour occuper tout l'écran ou une
partie de celui-ci.
 L’éditeur graphique d’Android Studio vous permet de placer et configurer
différents éléments graphiques sur l’écran et générer le contenu XML.
Maintenant que vous avez fait connaissance avec l’éditeur graphique, il est temps de
créer l’interface du premier écran de SuperQuiz ! Ce sera au prochain chapitre.

V-Dessinez l’interface de votre premier écran

Les conteneurs
Pour afficher des éléments à l'écran et les organiser entre eux, il est impératif
d'utiliser un conteneur.

Dans le fichier XML de mon layout fragment_welcome, le premier élément que nous
voyons est du type FrameLayout . Cet élément est un conteneur. Android suffixe
toujours le nom des conteneurs par Layout.
Parmi les conteneurs proposés par Android, nous pouvons noter par exemple :

P a g e 29 | 109
Programmation Mobile Android MOUOMENE

 FrameLayout: permet de positionner les éléments les uns au-dessus des


autres ;
 LinearLayout: permet de positionner les éléments les uns à la suite des
autres, dans le sens horizontal ou vertical ;
 ConstraintLayout: permet de positionner les éléments les uns par rapport aux
autres en utilisant des règles de positionnement ;
 RelativeLayout: c’est l’ancêtre de ConstraintLayout, il permet également de
positionner les éléments les uns par rapport aux autres.
Sachez que pour positionner des éléments les uns par rapport aux autres, Google
encourage fortement d’utiliser ConstraintLayout à la place du
layout RelativeLayout , pour des raisons de performance et pour un meilleur
support.
Pour concevoir l’écran d’accueil de notre application tel que sur la maquette suivante,
remplacez la balise XML FrameLayout par LinearLayout . Pour que l’agencement
des éléments se fasse de manière verticale, il faut ajouter
l’attribut android:orientation="vertical" . Vous devez obtenir le résultat suivant :

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".WelcomeFragment">

<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment" />

</LinearLayout>

Les attributs
Occupation de l'espace

Les deux attributs d’une balise XML fondamentaux


sont layout_width et layout_height . Ils définissent comment afficher un élément
au sein de son conteneur. Les quatres valeurs possibles sont :
 match_parent : l'élément doit occuper tout l’espace disponible offert par son
parent (vous pourriez voir apparaître de temps en temps fill_parent au
détour d'un tutoriel ou d'un site web : c'est un attribut obsolète, ancêtre
de match_parent ) ;
 wrap_content : l'élément doit n'occuper que la place nécessaire à l'affichage
de son contenu ;
 0dp : la taille de l’élément est définie par ses contraintes avec un conteneur
de type ConstraintLayout ;
 la valeur brute en dp, même s’il est plutôt recommandé d’utiliser les trois
options précédentes pour un rendu plus “responsive”.

P a g e 30 | 109
Programmation Mobile Android MOUOMENE

L'unité de mesure dp permet de s'affranchir des incohérences d'affichage suivant la


résolution des téléphones. Vous pouvez lire cette page pour mieux comprendre
cette notion.
Gravitation

La notion de gravitation peut s'appliquer à un élément ou à son contenu. Elle


permet de déterminer comment positionner un élément par rapport à son conteneur,
ou comment positionner les enfants d'un élément.

Pour définir unitairement le positionnement d'un élément au sein de son layout


parent, c'est l'attributandroid:layout_gravityqu'il faut utiliser. Les valeurs possibles
sont nombreuses : start , end , center , center_vertical , center_horizontal ,
etc.
Pour définir globalement le positionnement des éléments enfants d’un conteneur,
c'est l'attributandroid:gravity qu'il faut utiliser. Les valeurs possibles sont identiques
à l'attribut layout_gravity .
Texte

Pour définir du texte, nous allons utiliser l’attribut android:text . Nous pouvons y
saisir des textes bruts directement, comme par exemple android:text=”Hello” , ou
utiliser des chaînes de caractères internationalisées. Celles-ci doivent être spécifiées
dans un fichier strings.xml présent dans le répertoire res/values/ . Pour le
moment, notre projet ne comporte qu’une seule langue, et donc il y a un seul
fichier strings.xml . Voici le contenu de ce fichier dans mon projet :

<resources>
<string name="app_name">SuperQuiz</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>
Ainsi, dans le code du layout XML, au sein du composant TextView , pour utiliser la
chaîne de caractères nomméehello_blank_fragment , il suffit
d’écrire android:text="@string/hello_blank_fragment" .
Dans la suite de ce cours, pour faciliter la lisibilité, j’utiliserai des chaînes de
caractères en dur plutôt qu'issues du fichier strings.xml . Sachez qu’il est
préférable d’extraire tous les textes de votre application, comme décrit
précédemment, pour faciliter son internationalisation.
L'élément TextView possède également d’autres propriétés. Nous utiliserons les
propriétés suivantes dans ce cours :
 android:textSize : pour changer la taille du texte. L’unité attendue est de
type sp , qui signifie scale-independant pixels. Cette unité se base sur la
densité de pixels de l’appareil, mais prend aussi en compte la taille de la
police définie dans les réglages de l’appareil. Cela permet à l’interface
d’afficher le texte en plus en moins gros selon ce réglage, et donc de rendre
votre application plus accessible ;
 android:textStyle : pour mettre le texte en gras ou en italique (soit
respectivement la valeur bold ou italic ) ;
 android:background : pour changer sa couleur de fond.
Si cela vous intéresse, vous pouvez consulter la liste des attributs XML de
l'élément TextView dans la documentation officielle.
Marge

P a g e 31 | 109
Programmation Mobile Android MOUOMENE

Pour décoller un élément légèrement du bord, nous pouvons utiliser


l'attribut android:layout_margin , permettant de préciser une grandeur de marge.
Si vous ne souhaitez modifier qu'une seule marge, vous pouvez utiliser les attributs
suivants : layout_marginTop , layout_marginBottom , layout_marginStart ou layou
t_marginEnd .
Padding

Le padding consiste à ajouter de l'espace entre le contenu d'un élément et les bords
de cet élément.

Amusez-vous à ajouter une marge et un padding à l'élément TextView de votre


projet, et à modifier le texte, afin d’obtenir le résultat illustré ci-dessous.

Le padding consiste à ajouter de l'espace entre le contenu d'un élément et les bords
de cet élément, le margin consiste à ajouter de l'espace entre un élément et son
parent.
L’attribut android:background est également utilisé dans cet extrait pour mettre en
évidence la différence entre la marge et le padding.
Voici le code de l’élément TextView que vous avez dû écrire :
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Codez comme vous êtes !"
android:layout_margin="60dp"
android:padding="30dp"
android:background="#d2c2fd"/>```
Voilà, vous en savez suffisamment sur la mise en page des éléments et sur
l'élément TextView , pour pouvoir (enfin !) dessiner la partie supérieure du premier
écran de notre application SuperQuiz.

À vous de jouer

P a g e 32 | 109
Programmation Mobile Android MOUOMENE

Vous allez maintenant développer la partie supérieure du layout correspondant au


fragment WelcomeFragment . Concrètement, vous allez ajouter les deux premiers
textes de cet écran et les agencer correctement au sein d’un LinearLayout .
Essayez de reproduire seul la maquette ci-dessous au sein du
fichier fragment_welcome.xml . Concernant la taille des textes et l’espacement entre
les éléments, vous pouvez utiliser les valeurs qui vous semblent les plus homogènes.

Maquette de la partie supérieure de l’écran d’accueil.


Dans la vraie vie de développeur ou de développeuse, les valeurs précises
concernant les tailles ou les marges à utiliser pour réaliser un écran sont fournies par
la personne de l’équipe s’occupant de concevoir le design de l’application, en
utilisant par exemple un outil comme Figma.
Alors, vous avez réussi ? Si vous avez besoin d’un peu d’aide, voici les attributs clés
à utiliser pour obtenir un affichage équivalent à notre maquette :

P a g e 33 | 109
Programmation Mobile Android MOUOMENE

Maquette de la partie supérieure de l’écran d’accueil avec indication d’attributs pour


chaque élément
Allez, c’est parti pour la correction. Au final, le code de votre
layout fragment_welcome.xml devrait correspondre à ça :

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.WelcomeFragment"
android:gravity="center_horizontal"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"

P a g e 34 | 109
Programmation Mobile Android MOUOMENE

android:text="Welcome to SuperQuiz"
android:textSize="24sp"
android:layout_margin="24dp"/>

<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="What's your name"
android:textSize="20sp"
android:layout_margin="24dp"/>
</LinearLayout>

Ajoutez les éléments de contrôle


Nous allons maintenant ajouter les deux derniers éléments présents sur l’écran
d’accueil de notre application, soit un champ de saisie et un bouton.

Maquette de l’écran d’accueil complété avec le champ de saisie et le bouton “Let’s


play”

Champ de saisie
Pour que l'utilisateur puisse taper son nom, il faut lui présenter un élément lui
permettant de saisir du texte. Sous Android, c'est l'élément EditText qui porte ce
rôle. Pour ce faire, toujours dans le fichier fragment_welcome.xml , ajoutez un
élément EditText .
Dès que vous commencez à saisir le chevron et les premières lettres, Android Studio
vous propose automatiquement tous les choix possibles. Dès que vous voyez
P a g e 35 | 109
Programmation Mobile Android MOUOMENE

apparaître EditText , sélectionnez-le et appuyez sur la touche Entrée ou la touche


de tabulation.
Android Studio vous ajoute ensuite automatiquement les
attributs layout_width et layout_height . Il vous positionne également le curseur
sur la première valeur.
Commencez à saisir les premières lettres de match_parent et complétez
automatiquement en appuyant sur Entrée ou Tabulation. Passez à la
ligne layout_height automatiquement avec Tabulation ou Entrée , et mettez cette
fois wrap_content comme valeur. Enfin, appuyez sur Entrée ou Tabulation pour
“sortir” des guillemets, et fermez l’élément XML avec la touche / . Android Studio
s’occupe de rajouter le chevron manquant, c'est magique !
Tout comme pour le champ texte, nous pouvons ajouter une marge afin d'éviter que
la zone de saisie ne soit trop proche des bords, par exemple à gauche et à droite.
Par exemple, une marge de 16 dp.

Il est également possible d'ajouter un indice à l'aide de l'attribut android:hint . Cet


indice apparaît dans le champ texte pour apporter une information à l'utilisateur, puis
disparaît dès qu'il commence à saisir du texte.
Vous devez obtenir le résultat suivant (seul l'élément EditText est présenté dans cet
extrait de code) :

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:minHeight="48dp"
android:hint="Please type your name"/>
Une bonne pratique d’accessibilité consiste à s’assurer que tout contenu interactif ait
une taille supérieure à 48 dp en hauteur et en largeur. C’est pour cela que dans
l’extrait de code précédent, l’attribut minHeight est également renseigné, assurant
que la hauteur de l’élément soit suffisamment haute. Nous n’avons pas de soucis
d’accessibilité en largeur ici.

Bouton
Maintenant que l'utilisateur s'est présenté, il n'a plus qu'à appuyer sur un bouton pour
commencer à jouer. L'élément XML à utiliser est Button .
Comme pour l'élément TextView , l'attribut à utiliser pour spécifier le titre
est android:text . Nous décidons de nommer le bouton "Let's play" . Vous pouvez
modifier la marge ou le padding pour positionner le bouton comme bon vous semble.
Pour ma part, j'ai décidé de lui affecter un padding horizontal de 24 dp, pour que le
bouton soit plus large, et de l’espacer de ses voisins avec une marge de 16 dp. Voici
le résultat :
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:paddingHorizontal="24dp"
android:textSize="14sp"
P a g e 36 | 109
Programmation Mobile Android MOUOMENE

android:text="Let's play" />

En résumé
 Une activité ou un fragment utilise un fichier layout pour pouvoir positionner
ses éléments graphiques à l’écran.
 Il y a plusieurs types de layouts permettant d’agencer différemment les
éléments graphiques d’un écran : de manière relative aux autres éléments, de
manière horizontale, de manière verticale ou en superposition.
 Pour bien affiner la position et l’espace occupé par des éléments à l’écran, il
faut utiliser les différents attributs disponibles, par
exemple margin , padding , layout_gravity , etc.

VI-Accédez aux éléments interactifs depuis le code


Java de votre fragment
Il est temps maintenant de se pencher sur le code Java associé à notre fragment
d’accueil. Ouvrez le fichierWelcomeFragment situé dans le
répertoire java/nom.de.votre.package .

Déterminez les éléments interactifs de l’interface


Rappelez-vous, nous avons créé la classe WelcomeFragment dans la partie
précédente. Cette classe contient du code par défaut. En particulier, la méthode
suivante est déjà implémentée :
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
Cette méthode onCreate() est appelée lorsque le fragment est créé. C’est au sein
de cette fonction que nous allons ajouter du code, notamment pour récupérer les
éléments déclarés dans notre layout pour pouvoir interagir avec.
Pour rappel, nous avons quatre éléments graphiques dans notre interface :

1. Le texte d'accueil.
2. Le texte demandant de saisir le nom de l’utilisateur.
3. Le champ de saisie du nom.
4. Le bouton de lancement du quiz.
Nous avons besoin d'interagir avec les deux derniers éléments de cet écran :

 le champ de saisie afin de récupérer son contenu ;


 le bouton de démarrage pour savoir quand l’utilisateur clique dessus.
Pour pouvoir rendre ces éléments dynamiques, nous allons devoir obtenir une
référence aux éléments déclarés dans le code XML du fragment depuis le code Java.

Référencez les éléments graphiques


Configuration de View Binding

P a g e 37 | 109
Programmation Mobile Android MOUOMENE

Il existe plusieurs moyens de référencer des éléments graphiques sur Android.


Historiquement, c’est via la fonction findViewById() que le SDK d’Android permet
de faire ça dès la première version d’Android.
Mais depuis, Google a également développé deux bibliothèques permettant de faire
la même chose, mais plus facilement : Data Binding et View Binding. Dans ce
cours, nous allons utiliser View Binding, qui est plus simple à mettre en place, et
plus populaire.

Pour s’en servir, il faut au préalable activer cette fonctionnalité au sein de notre projet
Android. Dans l’arborescence de fichiers, dans la section Gradle Scripts, ouvrez le
fichier build.gradle correspondant au module :app de votre projet.

Cliquez sur le fichier build.gradle


Ce fichier devrait ressembler à ça :
plugins {
id 'com.android.application'
}

android {
namespace 'fr.mycompany.superquiz'
compileSdk 33

defaultConfig {
applicationId "fr.mycompany.superquiz"
minSdk 24
targetSdk 33
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8

P a g e 38 | 109
Programmation Mobile Android MOUOMENE

}
}

dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Pour utiliser la fonctionnalité View Binding dans votre projet, ajoutez simplement le
bloc buildFeature suivant au sein du bloc android , par exemple juste après le
sous-bloc compileOptions :
android {

compileOptions { … }
buildFeatures {
viewBinding = true
}
}
Une fois que c’est fait, un bandeau apparaît alors en haut de l’éditeur, vous invitant à
synchroniser les fichiers de votre projet. Cliquez sur le bouton Sync Now.

Cliquez sur le bouton Sync Now


Vous verrez en bas à droite de l’IDE que celui-ci est en train d’effectuer tout un tas
de chargements et de mises à jour. Attendez que ce chargement soit terminé. Cela
ne devrait pas durer trop longtemps.

Attendez la fin de la synchronisation du projet


Identification des éléments graphiques

P a g e 39 | 109
Programmation Mobile Android MOUOMENE

Pour pouvoir référencer depuis le code Java le champ de saisie du nom de


l’utilisateur et le bouton de lancement du quiz de notre application, il manque
néanmoins une petite chose dans les attributs de ces éléments : un identifiant !

Pour cela, l'attribut à utiliser est android:id , et sa valeur doit


être "@+id/votre_identifiant" . Notez bien que l'identifiant doit toujours être
préfixé par "@+id/" . Par exemple, dans le fichier fragment_welcome.xml :
<EditText
android:id="@+id/usernameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:minHeight="48dp"
android:hint="Please type your name"/>

<Button
android:id="@+id/playButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:paddingHorizontal="24dp"
android:textSize="14sp"
android:text="Let's play" />
Vous êtes libre d'utiliser le nom d'identifiant que vous souhaitez, mais attention : un
identifiant doit être unique au niveau du projet, ce qui signifie que vous ne
pouvez pas utiliser le même identifiant dans deux fichiers séparés.
Instanciation du View Binding au sein du fragment
En activant View Binding pour notre application, nous avons activé un mécanisme
qui va générer automatiquement une classe de liaison pour chaque fichier XML de
type layout présent dans notre projet. Ce sont ces classes de liaison qui vont nous
permettre d’accéder aux éléments identifiés dans nos layouts depuis le code Java.

Par défaut, le nom de ces classes de liaison correspond au nom du fichier layout
converti en Pascal Case et suffixé du mot “Binding”. Par exemple, pour le
fichier fragment_welcome.xml , la classe de liaison associée
est FragmentWelcomeBinding.java .
Ces classes de liaison, également appelées classes de binding, ne sont pas visibles
dans l’arborescence de votre projet. Ne vous inquiétez pas, c’est normal.
Pour utiliser View Binding au sein de votre fragment d’accueil, commencez par créer
une variable globale de type FragmentWelcomeBinding au sein de la
classe WelcomeFragment :
public class WelcomeFragment extends Fragment {

private FragmentWelcomeBinding binding;

/*...*/
}
Au fur et à mesure que vous tapez FragmentWelcomeBinding , l’autocomplétion
d'Android Studio vous suggère directement la classe correspondante.

P a g e 40 | 109
Programmation Mobile Android MOUOMENE

Autocomplétion d'Android Studio pour la classe FragmentWelcomeBinding


Ensuite, rendez-vous dans la fonction onCreateView présente dans votre
fragment WelcomeFragment . Pour l’instant, cette fonction ressemble à ça :
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_welcome, container, false);
}
Rappelez-vous, c’est dans cette fonction que la liaison entre la classe Java et le
fichier XML contenant la description de la vue du fragment est faite par défaut. Afin
d’utiliser View Binding, nous allons modifier le contenu de cette fonction, pour
indiquer au système qu’il doit maintenant passer par la classe de liaison.

Pour cela, remplacez le contenu de cette fonction par le code suivant :

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentWelcomeBinding.inflate(inflater, container, false);
return binding.getRoot();
}
Dans cet extrait, vous pouvez également constater que la variable binding que nous
avons déclarée plus tôt est ici valorisée.

Accès aux éléments graphiques depuis le code Java


Nous allons maintenant pouvoir accéder aux deux éléments qui nous intéressent
dans le code Java. Pour cela, au sein de la classe WelcomeFragment , vous allez
surcharger la fonction onViewCreated . Ensuite, au sein de cette fonction,
commencez à saisir binding. , puis observez l’autocomplétion proposée par Android
Studio.

Autocomplétion d'Android Studio listant les éléments accessibles depuis le code


Java du fragment WelcomeFragment via la classe de liaison
Vous constaterez que nous pouvons accéder à tous les éléments ayant un identifiant
au sein de notre layout, en particulier les
éléments playButton et usernameEditText qui nous intéressent.
P a g e 41 | 109
Programmation Mobile Android MOUOMENE

Amusez-vous à changer le texte du bouton de lancement du quiz, comme dans


l’extrait de code suivant :
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
binding.playButton.setEnabled(false);
}
Pour visualiser vos modifications, lancez l’application sur un appareil.

Utilisation de View Binding au sein d’une activité


Pour utiliser View Binding au sein d’une activité et non pas un fragment comme dans
notre cas, la liaison entre le layout XML et le code Java ne se fait pas au sein de la
fonction onCreateView , mais au sein de la fonction onCreate .
Par exemple, pour utiliser View Binding au sein de l’activité
principale MainActivity de notre application, il faut instancier une variable globale
de type ActivityMainBinding , puis remplacer la ligne de
code setContentView(R.layout.activity_main); présente dans la
fonction onCreate , par le code suivant :
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
Vous devriez obtenir à la fin une classe MainActivity qui ressemble à cela :
public class MainActivity extends AppCompatActivity {

}
Lorsque vous commencerez à saisir un type (par exemple View ), vous constaterez
qu'il apparaît en rouge. Pourquoi ? Parce que ce type est inconnu : il faut importer la
classe où ce type est défini. Par exemple, pour le type View , il faut importer la
classe View située dans le package android.view , en saisissant la ligne suivante
en haut du fichier : import android.view.View; .
Heureusement, Android Studio est là pour vous prêter main-forte : dès qu'il détecte la
saisie d'un type inconnu, il vous propose d'importer automatiquement la classe
correspondante en appuyant sur ALT + Entrée sur PC ou Option + Entrée sur Mac.
Récapitulons en vidéo
Retrouvez ces différentes étapes dans la vidéo ci-dessous :

En résumé
 Pour dynamiser un élément graphique déclaré dans un layout, il faut d’abord
lui associer un identifiant unique grâce à l’attribut android:id .
 Pour accéder à un élément graphique déclaré en XML depuis du code Java,
la solution la plus simple et la plus populaire s’appelle View Binding.
 Pour utiliser View Binding, il faut tout d’abord l’activer dans le
fichier build.gradle de son application.
P a g e 42 | 109
Programmation Mobile Android MOUOMENE

 Avec la fonctionnalité View Binding, pour chaque fichier XML de type layout
présent dans le projet, une classe de liaison est générée automatiquement.
 Au sein du code Java, il faut déclarer une variable globale du type de cette
classe de liaison. Le nom de cette classe correspond au nom du layout
converti en Pascal Case et suffixé par Binding.
 Au sein d’un fragment, il faut spécifier que nous utilisons la classe de liaison
pour lier le code XML au code Java dans la fonction onCreateView . Au sein
d’une activité, cela se fait dans la fonction onCreate .
 Enfin, pour accéder aux éléments d’interface dans le code Java, il suffit de
saisir binding.nomDeLelement , où binding correspond au nom de la variable
globale, et nomDeLelement est l’identifiant d’un élément graphique du layout
présent dans l’attribut android:id.

VII-Gérez les actions de l’utilisateur

Dans ce chapitre, nous allons apprendre en particulier comment interagir avec


l'utilisateur et répondre à ses différentes actions.

Les deux actions les plus importantes à implémenter sur l’écran d’accueil sont les
suivantes :

1. Vérifier la saisie du nom de l'utilisateur.


2. Détecter le clic sur le bouton pour démarrer le jeu.
Vérifiez la saisie de l’utilisateur
Il faut s'assurer que l'utilisateur ait saisi son prénom avant de démarrer le quiz. Pour
l'empêcher d'aller plus loin, la façon la plus simple consiste à désactiver le bouton de
démarrage de jeu au lancement de l'application, puis de l'activer lorsqu'il a saisi son
prénom.

Tout d'abord, dans le fichier WelcomeFragment , ajoutez la ligne suivante dans la


méthode onStart() : binding.playButton.setEnabled(false); . Elle permet de
désactiver le bouton de l'interface. Par défaut, le composant Button est bien fait,
puisque du point de vue de l’utilisateur, il apparaît dans une autre couleur et paraît
bien désactivé.
Assurez-vous d'ajouter cette ligne dans la méthode onstart() et pas dans la
fonction onCreate . Sinon vous appellerez la méthode setEnabled() sur un objet
non défini, ce qui va naturellement faire planter l'application. En effet,
l’objet binding est valorisé au sein de la fonction onCreateView() . Dans le chapitre
suivant, un rapide coup d'œil au cycle de vie d’un fragment vous permettra de
comprendre pourquoi l’objet binding est encore nul dans la fonction onCreate() .
Ensuite, il faut pouvoir être notifié lorsque l'utilisateur commence à saisir du texte
dans le champ EditText correspondant. Pour ce faire, nous allons appeler la
méthode addTextChangedListener() sur l’élément EditText , en utilisant une
implémentation d'interface sous forme de classe anonyme.
Voici le code que vous allez ajouter dans onViewCreated() , à la suite du code
existant :
binding.usernameEditText.addTextChangedListener(new TextWatcher() {

P a g e 43 | 109
Programmation Mobile Android MOUOMENE

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

@Override
public void afterTextChanged(Editable s) {

}
});
Afin de vous éviter de tout taper, Android Studio vous proposera des suggestions
lorsque vous commencerez à taper addText ... vous pourrez alors appuyer sur la
touche de tabulation ou Entrée pour compléter automatiquement.
D'autre part, lorsque vous commencerez à saisir ce qu'il y a après entre les
parenthèses, arrêtez-vous après avoir tapé le T de TextWatcher : Android Studio va
gentiment vous proposer d'implémenter automatiquement l'interface associée.
Appuyez sur Tabulation ou Entrée et là, miracle !

Android vous propose d'implémenter automatiquement l'interface associée


La méthode qui va nous intéresser est celle à la fin : afterTextChanged .
Si vous constatez que les méthodes générées par Android Studio possèdent des
signatures étranges (telles que celle ci-dessous avec i1 ), c'est qu'il vous manque le
code source d'Android.
beforeTextChanged(CharSequence charSequence, int i1, int i2, int i3)
En effet, lorsque vous demandez à Android Studio d'implémenter l'interface pour
vous, il va analyser le code source de l'interface pour récupérer les méthodes à
implémenter, et récupérer les noms des paramètres correspondants. Si le code
source n'est pas installé, il va générer des noms sans aucun sens pour le
développeur.

Pour installer le code source, rendez-vous dans les préférences d'Android


(raccourci ⌘ + virgule sous Mac, ou CTRL + ALT + S sous Windows) puis dans la
section Appearance & Behavior > System Settings > Android SDK, ou lancez
directement le SDK Manager depuis l'IDE :

P a g e 44 | 109
Programmation Mobile Android MOUOMENE

Ensuite, dans l'onglet SDK Platforms, cochez la case Android 11.0 (R) (ou le
numéro correspondant à votre version), puis appuyez sur le bouton Apply.
À chaque fois que l'utilisateur saisira une lettre, la méthode afterTextChanged sera
appelée. Cela nous permettra de déterminer si l'utilisateur a commencé à saisir son
nom, et ainsi activer le bouton de démarrage de jeu.
La logique à appliquer est donc simple : si au moins une lettre a été saisie, alors le
bouton doit être actif, sinon il est inactif.

Pour activer ou désactiver un bouton, nous pouvons faire appel à la


méthode setEnabled , qui est un setter qui permet de modifier le statut d’activation
du bouton. Ce setter prend en paramètre un booléen.
Notre condition est de savoir si le texte saisi par l'utilisateur est vide. Le texte entré
par l'utilisateur est représenté par la variable s . Pour la convertir en chaîne de
caractères et pouvoir la manipuler, nous pouvons appeler la méthode toString .
Nous pouvons ensuite appeler la méthode isEmpty qui nous permet de savoir si
cette chaîne est vide ou non.
Dans notre cas, nous voulons un texte non vide, donc nous rajoutons un ! qui
permet d'inverser la valeur du booléen renvoyé par la fonction isEmpty .
Le résultat final sera
: "binding.playButton.setEnabled(!s.toString().isEmpty());""

Détectez le clic sur le bouton


Une fois le bouton activé, l'utilisateur peut cliquer dessus pour lancer le quiz (nous
verrons comment lancer le quiz dans la partie suivante). Pour détecter que
l'utilisateur a cliqué sur le bouton, il est nécessaire d'implémenter un listener.

Le principe est identique à la gestion de la saisie présentée au-dessus. Il faut pour


cela appeler la méthodesetOnClickListener() sur l'objet playButton , puis
implémenter l'interface OnClickListenerde la classe View , comme ceci :
binding.playButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// The user just clicked
}
});
La méthode onClick() est appelée chaque fois que l'utilisateur appuie sur le bouton.

En résumé
 Il est possible de gérer les attributs des composants dans le fragment (ou
l’activité) pour les dynamiser à la suite d’une action de l’utilisateur.
 Pour détecter que l'utilisateur a cliqué sur le bouton, il est nécessaire
d'implémenter un View.OnClickListener .

P a g e 45 | 109
Programmation Mobile Android MOUOMENE

 La méthode à utiliser pour détecter un changement de texte dans


un EditText est afterTextChanged() .

VIII-Créez un second fragment et naviguez vers celui-ci

Dans la première partie de ce cours, nous avons créé un premier fragment correspondant à
l’écran d’accueil de notre application, et permettant de récupérer le nom de l'utilisateur et de
démarrer le jeu. Dans cette deuxième partie, nous allons créer un second fragment. Ce
fragment permettra d'afficher le contenu du quiz. Il sera démarré par le premier fragment
lorsque l'utilisateur cliquera sur le bouton de lancement du quiz.

Vous vous demandez sûrement comment fonctionne le quiz ?

C'est très simple : SuperQuiz va poser une série de quatre questions à l'utilisateur, choisies
aléatoirement dans une banque de plusieurs questions. Pour chaque question, il aura le choix
entre quatre réponses possibles. Lorsque l’utilisateur cliquera sur une réponse, le bouton
changera de couleur de fond selon que la réponse est correcte ou non, et un message
complémentaire s’affichera en bas, indiquant de manière textuelle si la réponse est bonne ou
non. Enfin, un bouton apparaît également en bas pour permettre à l’utilisateur de passer à la
question suivante. Cela donne par exemple la maquette suivante :

P a g e 46 | 109
Programmation Mobile Android MOUOMENE

Maquette du fragment QuizFragment illustrant l’affichage d’une question et des 4 réponses, et


l’évolution de cet écran lorsque l’utilisateur clique sur la bonne réponse

Le changement de couleur de fond du bouton ne suffit- il pas pour indiquer que la réponse est
bonne ? Pourquoi ajouter un texte “Good Answer” ? Tout simplement pour que l’application
soit accessible aux personnes qui ne voient pas ou perçoivent mal les couleurs, et qui ne
peuvent pas savoir si la réponse est correcte, si l’indication de succès ou d’échec repose
uniquement sur la couleur. Pour en savoir plus sur cette bonne pratique d'accessibilité, vous
pouvez consulter le référentiel d’accessibilité international appelé WCAG, et en particulier le
critère 1.4.1 – Utilisation de la couleur.

Configurez un nouveau fragment

Créez les fichiers

Pour rappel, un fragment est composé d'une classe Java et d’un fichier layout XML.
Positionnez-vous dans l'arborescence de votre projet, au niveau du répertoire dans lequel est
contenue la classe WelcomeFragment créée précédemment. Faites un clic droit dessus, puis
sélectionnez l'option New > Fragment > Fragment (Blank).

P a g e 47 | 109
Programmation Mobile Android MOUOMENE

Créez un nouveau Fragment

Une nouvelle fenêtre s'affiche, vous permettant de configurer le nom du fragment et de son
fichier layout. Sachant que ce fragment va gérer l’affichage du quiz, nous allons
l’appeler QuizFragment . Le nom du fichier layout associé doit être fragment_quiz .
Cliquez sur le bouton Finish.

Configurez le nom du fragment et de son fichier layout

Ouvrez maintenant la classe QuizFragment fraîchement créée par Android Studio. Par
défaut, Android Studio propose un squelette de fragment recevant des paramètres en entrée.
Dans notre cas, ça n’est pas utile. Un petit nettoyage s’impose. Vous pouvez supprimer le
code chargé de maintenir et de gérer ces arguments. Vous devriez à la fin avoir une
classe QuizFragment semblable au code ci-dessous :

public class QuizFragment extends Fragment {

P a g e 48 | 109
Programmation Mobile Android MOUOMENE

public static QuizFragment newInstance() {

QuizFragment fragment = new QuizFragment();

return fragment;

@Override
public void onCreate (Bundle savedInstanceState ) {
super .onCreate( savedInstanceState);

@Override
public View onCreateView( LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate( R.layout .fragment_quiz, container , false );
}
}

Créez le layout

L'interface de ce nouveau fragment va contenir les éléments suivants :

 l'intitulé de la question posée à l'utilisateur ;


 les quatre réponses possibles, chacune représentée par un bouton.

Ensuite, dès que l’utilisateur aura sélectionné une réponse, les éléments suivants apparaîtront :

 un texte indiquant si la réponse est correcte ou non ;


 un bouton permettant de passer à la question suivante ou de terminer le quiz.

Pour réaliser ce layout, ouvrez le fichier fragment_quiz.xml situé dans le répertoire


res/layout. Avant de commencer, oublions un instant que le texte et le bouton “Next” ne
s’affichent que sous certaines conditions, car ces conditions d’affichage seront gérées dans le
code Java du fragment.

P a g e 49 | 109
Programmation Mobile Android MOUOMENE

La première question que vous devez vous poser avant de coder est : quel type de conteneur
utiliser ? Puis quels composants utiliser pour représenter les éléments à l’écran, et avec quels
attributs pour obtenir le rendu de la maquette précédente ?

Essayez par vous-même, puis rendez-vous ensuite ci-dessous pour la correction.

Dans notre cas, nous devons utiliser deux LinearLayout imbriqués. Le premier avec une
orientation verticale et le second, horizontale. Nous utiliserons les composants TextView et
Button. Vous pouvez modifier le code comme suit :

<?xml version="1.0" encoding="utf-8"?>


<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
android:gravity="center_horizontal">

<TextView
android:id="@+id/question"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="16dp"
android:padding="4dp"
android:layout_weight="2"
android:background="#767573"
android:gravity="center"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
tools:text="Question ?" />

<Button
android:id="@+id/answer1"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="4dp"
android:paddingVertical="16dp"
android:paddingHorizontal="32dp"
android:textSize="20sp"
android:textColor="@color/white"
android:backgroundTint="#6200EE"
tools:text="Answer1" />

<Button
android:id="@+id/answer2"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="4dp"
android:paddingVertical="16dp"
android:paddingHorizontal="32dp"
android:textSize="20sp"
android:textColor="@color/white"
android:backgroundTint="#6200EE"

P a g e 50 | 109
Programmation Mobile Android MOUOMENE

tools:text="Answer2" />

<Button
android:id="@+id/answer3"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="4dp"
android:paddingVertical="16dp"
android:paddingHorizontal="32dp"
android:textSize="20sp"
android:textColor="@color/white"
android:backgroundTint="#6200EE"
tools:text="Answer3" />

<Button
android:id="@+id/answer4"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="4dp"
android:paddingVertical="16dp"
android:paddingHorizontal="32dp"
android:textSize="20sp"
android:textColor="@color/white"
android:backgroundTint="#6200EE"
tools:text="Answer4" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center">

<TextView
android:id="@+id/validityText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:layout_margin="8dp"
android:padding="16dp"
android:textSize="18sp"
tools:text="💪 Good Answer !"
android:textAlignment="textEnd"
android:visibility="invisible"
tools:visibility="visible"/>
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="#767573"
android:layout_margin="8dp"
android:padding="16dp"
android:textSize="18sp"
android:text="Next"
android:visibility="invisible"
tools:visibility="visible"/>
</LinearLayout>

</LinearLayout>

P a g e 51 | 109
Programmation Mobile Android MOUOMENE

Vous devez déjà être familier avec certains attributs, tels que les marges, le texte à afficher ou
les identifiants assignés aux éléments. Notez bien que les identifiants sont essentiels, ce sont
eux qui permettront de référencer les éléments depuis le code, et de les mettre à jour
dynamiquement. Néanmoins, d'autres attributs doivent vous interpeller.

L'attribut android:textColor permet de spécifier quelle couleur utiliser pour la police


d’écriture, pour un champ texte ou un bouton.

Pour attribuer une couleur à un élément, vous avez le choix entre trois valeurs possibles :
 une valeur hexadécimale, par exemple #7451eb pour un violet OpenClassrooms ;
 une ressource définie au niveau d'Android, par exemple @android:color/white ;
 une ressource définie au niveau de votre projet, par exemple @color/primary . Vous
pouvez créer cette valeur et lui associer une couleur dans le fichier colors.xml situé
dans le répertoire res/values.
Plus de précisions dans la documentation d'Android.

L'attribut android:background prend le même paramètre que textColor , mais permet


pour sa part de préciser la couleur d'arrière-plan de la majorité des éléments (bouton, champ
texte, layout, etc.).

L'attribut android:textStyle permet de préciser le style à utiliser pour une police. Vous
avez le choix entre bold (gras), italic (italique) ou normal , la valeur par défaut.

L’attribut android:visibility permet d’indiquer si un élément est visible ou non. Il y a


trois valeurs possibles pour ce paramètre : visible , invisible ou gone . La différence
entre invisible et gone est que dans le cas de “invisible”, l’élément est masqué, mais il
occupe tout de même à l’écran l’espace qui lui était alloué. Alors que pour gone , l’élément
est également masqué mais il n’occupe pas d’espace à l’écran (c’est comme si sa largeur et sa
hauteur devenaient nulles).

Vous constatez peut-être au sein des attributs la présence d’un nouveau préfixe, tools . Par
exemple, tools:text et tools:visibility . Il permet dans ce cas d’usage de
personnaliser un attribut uniquement pour la prévisualisation qui en est faite dans Android
Studio. C’est très pratique pour développer.

Enfin, le dernier attribut très important est android:layout_weight . C'est un attribut


spécifique aux enfants d'un LinearLayout . Il permet de préciser le poids d'un élément par

P a g e 52 | 109
Programmation Mobile Android MOUOMENE

rapport aux autres. Je m'explique. Si vous assignez un poids identique pour tous les éléments
(par exemple 1), tous ces éléments auront la même taille. Si vous assignez un poids égal à 2 à
l'un des éléments, alors sa taille sera deux fois plus importante que celle des autres. Essayez
de faire varier le poids des éléments du code ci-dessus, afin de voir le résultat. N'hésitez pas à
consulter cette page pour avoir une explication plus détaillée.

Lorsque vous utilisez l'attribut layout_weight pour un élément donné, vous devez
impérativement spécifier une hauteur ou une largeur égale à 0 dp, suivant l'orientation. Plus
précisément, si l'orientation est verticale, la hauteur doit être égale à 0 dp. Si l'orientation est
horizontale, la largeur doit être égale à 0 dp.

Le résultat produit doit être similaire à la capture ci-dessous. Amusez-vous à modifier les
différents attributs pour donner un style qui vous est propre !

Prévisualisation de l’interface du fragment de quiz que vous devriez obtenir

P a g e 53 | 109
Programmation Mobile Android MOUOMENE

Lancez le nouveau fragment

C'est bien beau tout ça, mais comment afficher ce nouveau fragment ?

Rappelez-vous : nous avions commencé à implémenter la méthode onClick dans la


classe WelcomeFragment . Cette méthode est appelée à chaque fois que l'utilisateur appuie
sur le bouton. Et c'est à cet endroit précis que nous allons démarrer notre nouveau fragment.

Pour démarrer un nouveau fragment, Android met à disposition le FragmentManager . Cette


classe permet d’ajouter, de supprimer ou de remplacer un fragment au sein d’un hôte donné.
Elle s’occupe également de gérer la pile "Retour" associée. Pour accéder au
FragmentManager depuis un fragment, il faut appeler la
méthode getParentFragmentManager() .

Le fait de pouvoir ajouter, supprimer, remplacer un fragment suite à une action de l’utilisateur
s’appelle une transaction. Dans notre cas, pour pouvoir afficher le nouveau fragment
QuizFragment, il faut donc démarrer une transaction grâce à la
fonction beginTransaction() , puis créer un objet de type QuizFragment , et enfin ajouter
ce dernier fragment au sein de l’hôte prévu à cet effet, via l’objet de type transaction obtenu
précédemment avec la fonction beginTransaction() . Dans notre cas, l’hôte qui va
accueillir nos différents fragments est le conteneur FrameLayout portant l’identifiant
“container” au sein de l’activité principale de notre application. Voici le code correspondant à
ces explications :

FragmentManager fragmentManager = getParentFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

QuizFragment quizFragment = QuizFragment.newInstance();

fragmentTransaction.add(R.id.container, quizFragment);

fragmentTransaction.commit();

Ajoutez ces cinq lignes de code dans la méthode onClick . Puis lancez l'application, cliquez
sur le bouton, et vous devriez voir apparaître le nouveau fragment.

À noter que pour que l’utilisateur puisse retourner sur l’écran d’accueil en naviguant en
arrière depuis le fragment de quiz, il faut ajouter la ligne de code

P a g e 54 | 109
Programmation Mobile Android MOUOMENE

suivante : fragmentTransaction.addToBackStack(null); juste


avant fragmentTransaction.commit(); . Dans notre cas, nous ne souhaitons pas qu’il
puisse retourner à l’écran d’accueil une fois que le jeu a démarré, donc rien à faire.

Récapitulons en vidéo

Retrouvez ces différentes étapes dans la vidéo ci-dessous :

Revenez sur tout le code que nous avons ajouté dans la deuxième partie du cours avec la
branche y dédiée sur GitHub.

En résumé

 Pour créer un nouveau fragment dans un projet, il faut créer la classe Java et le layout
XML associé.
 Pour naviguer vers ce fragment, il faut utiliser le FragmentManager .
 Le FragmentManager permet de définir quel fragment ajouter, supprimer ou
remplacer, via une transaction. Votre deuxième fragment est maintenant créé. Bravo !

IX-Appréhendez le cycle de vie d’une application

Dans ce chapitre, nous allons voir comment fonctionne le cycle de vie d’une application
Android. C'est un sujet très important qui est très spécifique au développement mobile. En
effet, par rapport à un site web, une application sur un smartphone peut être dans différents
états. Par exemple, elle peut être démarrée et affichée à l’écran, ou masquée mais toujours
active en tâche de fond, affichée en mode portrait ou en mode paysage, etc. En tant que
développeurs et développeuses, nous devons être capables de détecter ces différents états dans
notre code afin de faire réagir en conséquence notre application.

Découvrez le cycle de vie d’une activité


Autre parallèle, lorsque votre réveil sonne le matin, vous passez de l'état endormi à l'état réveillé.
Ensuite, lorsque vous vous levez puis vous préparez à aller travailler, vous passez de
l'état réveillé à opérationnel. Tout au long d’une journée, vous passez d'un état à l'autre.
Sous Android, une activité passe également par différents états. Entre deux états, le système appelle
certaines méthodes bien spécifiques, dans un ordre déterminé.
Voici les différents états dans lesquels une activité peut se trouver :

P a g e 55 | 109
Programmation Mobile Android MOUOMENE

P a g e 56 | 109
Programmation Mobile Android MOUOMENE

Sous Android, l'activité passe par différents états entre lesquels le système appelle des méthodes
spécifiques dans un ordre déterminé

Par défaut, la méthodeonCreate()est automatiquement surchargée lorsque vous créez une


nouvelle activité. Mais comme vous pouvez le voir sur le diagramme ci-dessus, il en existe
d'autres, que nous allons découvrir maintenant.

La méthode onCreate()

Cette méthode est appelée lorsque votre activité est créée par le système, et qu'elle entre dans
l'étatCreated.

Généralement, les opérations effectuées dans cette méthode servent à mettre en place
l'interface graphique et à initialiser les données qui seront utilisées par l’interface.

À ce stade, l'activité est créée, mais l'utilisateur ne la voit pas encore et ne peut pas interagir
avec.

La méthode onStart()

Cette méthode est appelée par le système lorsque l'activité entre dans l'état Started.
L'interface graphique devient visible à l'utilisateur, mais il ne peut pas encore interagir avec
les différents éléments.

La méthode onResume()

Cette méthode est appelée lorsque l'activité entre dans l'étatResumed. L'activité devient
entièrement opérationnelle. L'utilisateur peut utiliser l'application et cliquer sur les différents
éléments graphiques.

L'application reste dans cet état tant qu'il n'y a pas d'interruption, comme par exemple la
réception d'un appel téléphonique, le démarrage d'une nouvelle activité ou l'affichage d'une
boîte de dialogue.

La méthode onPause()

Cette méthode est appelée lorsque l'activité entre dans l'étatPaused. Une Activity passe par
exemple dans l’étatPausedlorsqu’une AlertDialog est affichée : l’Activity sous-jacente est

P a g e 57 | 109
Programmation Mobile Android MOUOMENE

toujours visible, mais elle n’est pas au premier plan. Cette méthode est “l’inverse” de la
méthode onResume() : tout ce qui est initié dansonResume()doit être mis en pause dans cette
méthode. Par exemple, une animation présentée à l'utilisateur est démarrée
dansonResume()puis stoppée dansonPause().

Les traitements effectués dans cette méthode doivent être les plus courts possible.

La méthode onStop()

Cette méthode est appelée lorsque l'activité entre dans l'étatStopped. Par exemple, lorsqu'une
nouvelle activité est démarrée, l'activité appelante va se retrouver dans cet état. Elle n'est donc
plus du tout visible à l'utilisateur. Les traitements liés à la mise à jour de l'interface graphique
peuvent être arrêtés.

La méthode onDestroy()

Cette méthode est appelée lorsque l'activité est arrêtée.

Séchez vos larmes, c'est normal La première fois, cela effraie toujours un peu. Nous allons
mettre tout cela en pratique et vous irez tout de suite mieux.

Suivez le cycle de vie d’un fragment

Nous avons vu précédemment qu’un fragment correspond à une portion de l’interface. Il


possède également son propre cycle de vie, et celui-ci est étroitement lié à celui de l’activité à
laquelle il appartient. Entre deux états de son cycle de vie, le système appelle également
certaines méthodes bien spécifiques qui s'enchaînent comme illustré ci-dessous selon l’état de
l’application.

Partie 3

X-Structurez l’application avec l’architecture MVVM

Votre application à l’heure actuelle n'est pas très riche puisqu’elle affiche uniquement des
écrans statiques. Dans cette partie, nous allons nous charger de la rendre dynamique, et en
particulier l’écran qui affiche les questions du quiz. Nous allons par la même occasion

P a g e 58 | 109
Programmation Mobile Android MOUOMENE

apprendre à organiser notre code dans les règles de l’art, pour vous donner toutes les billes
par la suite pour construire des applications de qualité.

Découvrez l’architecture MVVM

Dans ce chapitre, nous allons introduire une notion d'architecture logicielle, afin de l'appliquer
à notre application SuperQuiz.

Lorsque vous développez une application Android, vous êtes libre d'organiser votre code
comme vous le souhaitez. Toutefois, pour faciliter la collaboration avec vos collègues,
structurer votre code et surtout vous assurer de sa maintenabilité, il est préférable d’utiliser
un patron d’architecture.

La raison d’être d’un patron d’architecture

Mais pourquoi vouloir structurer le code d’une application avec un patron d’architecture ? Ne
peut-on pas tout simplement écrire tout le code dans l'activité ou le fragment ?

Nous avons vu dans la partie 2 de ce cours que les activités et les fragments ont un cycle de
vie assez particulier. En effet, ils peuvent entre autres être détruits à n'importe quel moment,
emportant avec eux l'ensemble des données qu'ils contenaient. Cela peut se produire lors de
rotations, ou lorsqu'un appel téléphonique arrive, ou tout simplement lorsque le téléphone n'a
plus de batterie... Et ces cas de figure sont très compliqués à gérer pour nous, développeurs
et développeuses.

Voici d’autres situations considérées comme des changements de configuration sur


Android :
 passer en mode multi- fenêtre ;
 redimensionner l’application en mode multi- fenêtre ;
 plier un téléphone pliable ;
 changer le thème de l’appareil, tel que passer en mode sombre ou clair ;
 changer la taille de la police ;
 changer la langue de l’appareil.
Tous ces changements ont pour impact de détruire et de recréer l’activité ou le fragment.

De plus, si nous écrivons tout le code de notre application dans la classe d’une activité ou
d’un fragment, c’est-à-dire la déclaration des éléments d’interface, les interactions avec ces

P a g e 59 | 109
Programmation Mobile Android MOUOMENE

éléments, la récupération des données et leur stockage, nous créons ce que l’on appelle
du code spaghetti. C’est du code avec des interconnexions dans tous les sens, qui est difficile
à comprendre et à maintenir. Alors autant dans une assiette, les spaghettis c’est sympa, autant
dans le code d’une application, ça donne surtout des nœuds au cerveau.

Illustration d’un code spaghetti où tout est géré au sein de l’activité ou du fragment

C’est pour éviter ces situations qu’il existe différents patrons d’architecture, comme MVC,
MVI, MVP ou encore MVVM. En 2017, pour aider davantage les développeurs et
développeuses à structurer leur code, à faire un choix entre tous ces patrons, et pour
homogénéiser les pratiques utilisées au sein de l’écosystème Android, Google a décidé de
préconiser officiellement l’architecture MVVM.

Par la même occasion, ils se sont mis à partager tout un tas de bonnes pratiques et de
bibliothèques permettant d’organiser plus facilement le code d’une application Android. C’est
ce qui s’appelle l’Architecture Component.

Penchons-nous donc sur le patron d’architecture qui a gagné la bataille des patrons
d’architecture.

L’architecture MVVM

P a g e 60 | 109
Programmation Mobile Android MOUOMENE

MVVM signifie Model-View-ViewModel. Le principe de conception fondamental appliqué


par tout bon patron d’architecture, et en particulier par l’architecture MVVM, est
la séparation des responsabilités, séparant comme son abréviation l’indique les données
(Model) de la vue (View), et mettant à disposition une classe de
type ViewModel pour communiquer entre les données et la vue. C’est en appliquant ce
principe que nous sortons du patron “Code spaghetti”.

Les couches Vue et Données

MVVM isole donc dans le code deux parties distinctes. D’un côté les éléments correspondant
à la vue, de l’autre côté les éléments représentant les données de l’application. On appelle ces
différentes parties des couches d’architecture.

Ainsi, comme le montre le schéma ci-dessous :

 Dans la couche Vue, nous retrouvons la déclaration des éléments d’interface et


la logique UI, c'est-à-dire le code relatif uniquement à la manipulation de ces éléments
d’interface. Concrètement, la déclaration des éléments d’interface correspond au code
XML d’une activité ou d’un fragment. La logique UI correspond au code Java
responsable par exemple de la gestion du clic sur un bouton, de la gestion de la
visibilité d’un élément d’interface, du changement de couleur d’un élément, etc.
 Au sein de la couche Données, on retrouve bien entendu les données qui peuvent
provenir de sources diverses (base de données locale, API externe, etc), mais
également toute la logique métier permettant de récupérer ses données, de les modifier
et de les sauvegarder.

P a g e 61 | 109
Programmation Mobile Android MOUOMENE

Illustration des deux couches Vue et Données du principe de séparation des responsabilités appliqué
par le patron d’architecture MVVM

L’un des avantages d’une telle séparation est que maintenant la logique métier et l’accès aux
données sont centralisés, évitant ainsi la duplication de code. La couche de données peut ainsi
être utilisée depuis différents écrans. C’est le principe de conception de source de vérité
unique.
D’accord, mais comment les couches Vue et Données font-elles pour communiquer entre
elles ? En d’autres termes, à quoi correspond la flèche qui relie les deux couches sur le
schéma précédent ?

La réponse à cette question se trouve dans l’abréviation MVVM. Nous avons vu que le
premier “M” pour Model correspond à la couche Données. Le “V” correspond à la couche
Vue. Il nous reste donc “VM” pour ViewModel. Ce sigle désigne une classe de type
ViewModel qui est chargée de faire le lien entre la couche Vue et la couche Données.
ViewModel est en fait un pattern de développement qui n’est d’ailleurs pas spécifique à
Android.

Le pattern ViewModel

Le pattern ViewModel a pour rôle de préparer les données dont un écran de l’application aura
besoin (la couche Vue).

Il a deux objectifs sous-jacents :

1. Exposer à la Vue les données finales que l’interface doit représenter ; on appelle ces
données des états.
2. Mettre à disposition de la Vue des fonctions correspondant à des événements chargés
de modifier les données suite aux interactions ayant lieu sur l’interface.

Mais qu’est-ce qu’un état ?

Un état désigne toute valeur susceptible de changer au fil du temps. Que ce soit une
variable dans une classe, une valeur stockée en base de données, ou encore une valeur fournie
par une API tierce. Les écrans de nos applications Android sont, en fait, une représentation à
un instant t des différents états qui la composent.

Et qu’est-ce qu’un événement ?

P a g e 62 | 109
Programmation Mobile Android MOUOMENE

Un événement désigne toute modification pouvant avoir lieu dans une application et
ayant un impact sur l’état de l’interface : une interaction de l’utilisateur comme un clic sur
un bouton, une saisie, un scroll, mais également toute mise à jour d’un paramètre intrinsèque
à l’application ayant un impact sur celle-ci, par exemple un décompte qui se termine au sein
d’une application de type “Minuteur”.

Dans les faits, le pattern ViewModel se matérialise tout simplement par une classe, qui a
accès à la couche Données, et qui est chargée de gérer les événements et de personnaliser les
données dont a besoin l’interface. Si nous mettons à jour le schéma précédent avec ce
nouveau concept, cela donne :

Illustration des couches Vue et Données reliées par une classe de type ViewModel

P a g e 63 | 109
Programmation Mobile Android MOUOMENE

Le sens des flèches “États” et “Événement” sur le schéma précédent a son importance. Cela
représente le fait d’exposer des états depuis le ViewModel vers la Vue et de réceptionner
dans l’autre sens des événements générés par la Vue , et qui pourront être traités dans le
ViewModel. On applique ainsi un design pattern appelé flux de données unidirectionnel (ou
Unidirectional Data Flow – UDF). Ce pattern permet d’appliquer le principe de séparation des
préoccupations entre le ViewModel et la Vue.

En résumé

 Appliquer un patron d’architecture permet de structurer le code d’une application dans


le but de le rendre plus lisible, maintenable et testable.
 Il existe différents patrons d’architecture, mais Google préconise aujourd’hui
l’architecture MVVM.
 MVVM signifie Model-View-ViewModel.
 MVVM applique le principe de conception de séparation des préoccupations en
structurant le code d’une application en deux couches : Vue et Données, avec une
classe de type ViewModel pour faire la liaison entre ces deux couches et personnaliser
les données en fonction des besoins de la vue.
 Une classe de type ViewModel expose à la vue les données à afficher, appelé états, et
expose des fonctions permettant de manipuler l’état en réponses aux événements qui
ont lieu sur l’interface.

P a g e 64 | 109
Programmation Mobile Android MOUOMENE

Sous Android, un fragment passe également par différents états entre lesquels le système appelle des
méthodes spécifiques dans un ordre déterminé

Vous remarquez certainement que certaines de ces méthodes,


comme onCreate() , onStart ou onPause() , ont le même nom que celles appelées par
le système pour le cycle de vie d’une activité. Vous pouvez vous référer aux définitions
données précédemment pour comprendre dans quel état se trouve le fragment lorsque ces
méthodes sont appelées, en remplaçant bien sûr le terme activité par fragment. Définissons ci-
dessous les méthodes spécifiques au cycle de vie d’un fragment.

La méthode onAttach()

Cette méthode est appelée lorsqu’un fragment est ajouté à une activité. Elle est appelée une
seule fois durant toute la durée de vie du fragment.

La méthode onCreateView()
P a g e 65 | 109
Programmation Mobile Android MOUOMENE

Cette méthode est appelée par le système afin de créer l’interface utilisateur correspondant au
fragment. C’est en surchargeant cette méthode que nous indiquons au système la vue qu’il
doit dessiner précisément à l’écran.

Sur Android, le terme vue est souvent employé pour désigner l’interface graphique.

La méthode onViewCreated()

Cette méthode est appelée immédiatement après que le système a rattaché la vue au fragment.

La méthode onDestroyView()

À ce stade, la vue associée au fragment n’est plus affichée à l’écran. Mais l’objet fragment
existe toujours. C’est dans cette fonction que, si besoin, vous allez pouvoir nettoyer des
choses spécifiques à votre interface avant que le fragment soit complètement dans la
fonction onDestroy() qui suit.

La méthode onDetach()

Cette fonction est appelée après onDestroy() , et nous informe que le fragment a été
dissocié de son activité parente.

Mise en application

Pour bien comprendre l'enchaînement des méthodes, je vous invite à toutes les surcharger
(sauf onCreate() qui l'est déjà) dans votre activité et votre fragment, et d'afficher leur appel
dans la console. Pour rappel, l'affichage d'une trace en Android s'effectue de la sorte :

Log.d(TAG, "onStart() called");

Comme TAG, vous pouvez utiliser ce que vous voulez. Personnellement, j’utilise mon
prénom pour le retrouver facilement plus tard dans le LogCat !

En résumé

 Le cycle de vie d’une activité ou d’un fragment comporte différentes étapes qui se
déroulent entre sa création, son affichage à l’écran et sa suppression.

P a g e 66 | 109
Programmation Mobile Android MOUOMENE

 Les classes Activity et Fragment exposent des fonctions qui correspondent à ces
différentes étapes.
 En surchargeant ces fonctions, nous pouvons exécuter du code à des instants clés du
cycle de vie. Par exemple en surchargeant la fonction onCreate de la classe Activity
pour initialiser son contenu, ou dans la fonction onDestroy pour refermer une
connexion à la base de donnée.

Cela fait beaucoup de fonctions et beaucoup de concepts à retenir, mais bonne nouvelle pour
vous, au quotidien vous aurez surtout besoin de surcharger les
fonctions onCreate (et onCreateView et onViewCreated pour un
fragment), onStart() et onResume() . D’ailleurs vous l’avez déjà fait dans cette partie.

XI-Gérez les données du quiz

Créez le modèle de données

Avant de débuter la conception de la couche Données de l’architecture de notre application,


jetons un coup d'œil à la maquette ci-dessous, afin de comprendre comment doit fonctionner
le quiz de notre application.

P a g e 67 | 109
Programmation Mobile Android MOUOMENE

P a g e 68 | 109
Programmation Mobile Android MOUOMENE

 En cliquant sur une réponse (1), l’interface se met à jour pour indiquer à l’utilisateur si
la réponse est bonne ou non, et le bouton "Suivant" apparaît (2).
 En cliquant sur le bouton “Next” (2), l’interface se met à jour pour afficher une
nouvelle question. À nouveau l’utilisateur clique sur une réponse (3), etc.
 Lorsque l’utilisateur a répondu à la dernière question, le bouton “Finish” apparaît (4).
 En cliquant sur le bouton “Finish”, une fenêtre de dialogue apparaît pour indiquer à
l’utilisateur son score final (5). En cliquant sur “Quit”, le fragment QuizFragment est
remplacé par le fragment WelcomeFragment .

Pour concevoir cette fonctionnalité, nous devons modéliser l’objet représentant une
question du quiz. Cet objet se caractérise par :

 la question représentée par une chaîne de caractères ;


 les propositions représentées par une liste de chaînes de caractères ;
 la bonne réponse, représentée par un entier correspondant à l’index de proposition
correcte dans la liste précédente.

Pour modéliser cet objet, nous allons créer une classe Java nommée Question .

Lorsque nous mettons en place la couche Données d’une application, il est d’usage de placer
les fichiers relatifs à cette couche au même endroit, par exemple dans un package
nommé data .

Pour créer un nouveau package data dans Android Studio, faites un clic droit sur le package
principal de votre projet au sein de l’arborescence de fichier, puis allez dans le menu New et
sélectionnez Package.

P a g e 69 | 109
Programmation Mobile Android MOUOMENE

Créez un nouveau package avec Android Studio

Une fenêtre apparaît alors, vous permettant de saisir le nom du nouveau package. Complétez
le chemin actuel en ajoutant à la fin “data”.

Créez un nouveau package data avec Android Studio

Dans ce package, nous allons créer une classe Question . Pour cela, dans l’arborescence de
fichier, faites un clic droit sur le package data , puis allez dans le menu New et cliquez
sur Java Class. Saisissez “Question” dans la fenêtre qui apparaît puis tapez sur Entrée .

P a g e 70 | 109
Programmation Mobile Android MOUOMENE

Créez une classe Question

L'instance de cette classe contiendra une question, avec les quatre réponses associées et la
bonne réponse correspondante. De ce fait, elle va contenir les trois attributs privés suivants :

private final String question;

private final List<String> choiceList;

private final int answerIndex;

Pensez bien à importer la classe Java List comme ceci : import java.util.List; .

N'oubliez pas qu'en programmation, un index de tableau ou de liste commence toujours à 0.


Ainsi, si vous avez quatre réponses possibles et que la bonne réponse est la dernière de la
liste, l'index vaudra 3. Que se passerait-il si l'index valait 4 ?

Nous allons maintenant créer le constructeur et les fonctions getters et setters nécessaires
pour cette classe. Pour rappel, pour une variable privée donnée, une fonction de type getter va
permettre d’accéder à sa valeur, et une fonction de type setter de la modifier. Le constructeur
va nous permettre de créer un objet de type Question dans notre code.

Ne foncez pas tête baissée pour écrire à la main toutes ces fonctions. En effet, Android Studio
vous permet de générer toutes ces fonctions automatiquement. Pour cela, positionnez le

P a g e 71 | 109
Programmation Mobile Android MOUOMENE

curseur de votre souris sur le nom de la classe Question , puis faites un clic droit ou appuyez
sur ALT + Insert sur PC, ou ⌘ + N sur Mac. Sélectionnez Getter and Setter. Dans la
fenêtre qui s’affiche, sélectionnez les 3 variables et appuyez sur OK.

P a g e 72 | 109
Programmation Mobile Android MOUOMENE

P a g e 73 | 109
Programmation Mobile Android MOUOMENE

Générez les getters et setters des 3 variables privées de la classe Question

Renouvelez l’opération en sélectionnant cette fois-ci Constructor dans la liste, en prenant soin
de bien sélectionner les trois variables de la classe Question en tant que paramètres du
constructeur.

Vous devriez à la fin obtenir le code ci-dessous pour la classe Question :

public class Question {

private final String question ;


private final List<String> choiceList ;
private final Integer answerIndex;

public Question( String question , List<String> choiceList , int answerIndex ) {


this .question = question ;
this .choiceList = choiceList ;
this .answerIndex = answerIndex;
}

public String getQuestion () {


return question;
}

public List<String> getChoiceList () {


return choiceList ;
}

public Integer getAnswerIndex() {


return answerIndex;
}
}
Chouette, nous avons maintenant une classe Java qui va nous permettre de représenter l’objet
principal de notre quiz. Mais où sont les données ?

C’est justement l’objectif de la prochaine section !

Récupérez les données

Les données d’une application peuvent provenir de différentes sources :

 un fichier ;
 une base de données locale (par exemple via la bibliothèque Room) ;
 une API externe (par exemple via la bibliothèque Retrofit).

P a g e 74 | 109
Programmation Mobile Android MOUOMENE

Pour notre application, bien que dans la réalité, nous aurions certainement fait appel à une
API pour récupérer une liste de questions aléatoires, pour des raisons de simplicité, nous
allons créer une banque de questions “en dur” dans une classe Java.

Pour ce faire, dans le package data , nous allons créer une classe Java
nommée QuestionBank . Elle va exposer une fonction getQuestions() qui retournera une
liste de questions prédéfinie. Ci-dessous la classe que j'ai développée. Vous verrez, mes
questions ne sont pas très fun, alors n’hésitez pas à inventer vos propres questions.

public class QuestionBank {


public List<Question> getQuestions() {
return Arrays .asList(
new Question(
"Who is the creator of Android?" ,
Arrays .asList(
"Andy Rubin" ,
"Steve Wozniak" ,
"Jake Wharton" ,
"Paul Smith"
),
0
),
new Question(
"When did the first man land on the moon?" ,
Arrays .asList(
"1958" ,
"1962" ,
"1967" ,
"1969"
),
3
),
new Question(
"What is the house number of The Simpsons? ",
Arrays .asList(
"42" ,
"101" ,
"666" ,
"742"
),
3
),
new Question(
"Who painteddid the Mona Lisa paint?" ,
Arrays .asList(
"Michelangelo" ,
"Leonardo Da Vinci" ,
"Raphael" ,
"Caravaggio"
),
P a g e 75 | 109
Programmation Mobile Android MOUOMENE

1
),
new Question(
"What is the country top-level domain of Belgium?",
Arrays .asList(
".bg" ,
".bm" ,
".bl" ,
".be"
),
3
)
);
}
private static QuestionBank instance ;
public static QuestionBank getInstance() {
if ( instance == null ) {
instance = new QuestionBank();
}
return instance;
}
}

Si nous reprenons le schéma précédent, nous avons maintenant la couche Données qui
contient nos fameuses questions grâce à la classe QuestionBank .

P a g e 76 | 109
Programmation Mobile Android MOUOMENE

Illustration de la couche Données contenant maintenant QuestionBank

OK on avance, maintenant nous avons des données dans notre couche Données. Mais
comment fait-on pour y accéder depuis le ViewModel ?

Pour exposer les données dont l’application a besoin, nous allons utiliser le
pattern Repository (oui, je sais, encore un pattern…). Voyons à quoi il va servir.

Le pattern Repository

Ce pattern consiste à utiliser une classe intermédiaire pour servir de médiateur entre le
ViewModel et les différentes sources de données.

Mais en quoi avons-nous besoin de médiation ? Les données sont là, ne peut-on pas les
envoyer telles quelles en utilisant QuestionBank directement depuis le ViewModel ?

On pourrait effectivement utiliser QuestionBank directement depuis le ViewModel. Mais ici,


on va apprendre à faire les choses comme dans la vraie vie de développeur ou développeuse
en entreprise.

Pour comprendre la nécessité d’un médiateur tel que le Repository, revenons à notre couche
Données. Bien que dans notre cas les données proviennent d’une source unique via le
fichier QuestionBank , il est courant dans une application de devoir combiner plusieurs
sources de données. Typiquement, une base de données locale et une API externe.

Pour l’application SuperQuiz, nous pourrions par exemple imaginer dans le futur
de récupérer des questions sur internet depuis un serveur, et de les stocker dans une base
de données locale sur le téléphone. Cela faciliterait par exemple l’utilisation de
l’application hors connexion. C’est l’une des raisons qui justifie l’usage du pattern
Repository.

P a g e 77 | 109
Programmation Mobile Android MOUOMENE

Le pattern repository

Un Repository permet donc de :

 centraliser l’accès aux données ;


 gérer à un endroit unique la logique permettant de définir quelle source de données
utiliser.

Si nous reprenons notre schéma, cela donne ça :

P a g e 78 | 109
Programmation Mobile Android MOUOMENE

Illustration de la couche Données contenant maintenant la classe QuestionRepository

Il convient de créer un Repository pour chaque type de donnée que manipule une
application. Dans notre cas, pour le moment SuperQuiz manipule uniquement des questions.
Nous allons donc créer une classe QuestionRepository . Si plus tard notre application
manipule des données concernant l’utilisateur, par exemple via un objet User , nous pourrons
créer un Repository spécifique nommé UserRepository .
P a g e 79 | 109
Programmation Mobile Android MOUOMENE

À vous de jouer !

Maintenant, à vous de créer le Repository pour nos questions !

Dans le package data , créez une classe Java intitulée QuestionRepository .

Celle-ci prend en paramètre la source de données unique QuestionBank . N’oubliez pas de


déclarer le constructeur de cette classe.

Enfin, faites en sorte que cette classe expose une fonction getQuestions() qui retourne la
liste des questions issues de la banque de questions.

Essayez seul(e), avant de regarder la solution ci-dessous.

public class QuestionRepository {

private final QuestionBank questionBank;

public QuestionRepository(QuestionBank questionBank) {

this.questionBank = questionBank;

public List<Question> getQuestions() {

return questionBank.getQuestions();

En résumé

 Pour concevoir la couche Données d’une fonctionnalité, il faut identifier les modèles
de données spécifiques à l’application, et créer un objet Java pour chaque modèle.
 Il est d’usage de positionner tous les fichiers relatifs à la couche Données au même
endroit, par exemple dans un package data .
P a g e 80 | 109
Programmation Mobile Android MOUOMENE

 Les données d’une application peuvent provenir de différentes sources : un fichier, une
base de données locale, une API externe, etc.
 Une fois la provenance des données déterminée, la logique permettant d’arbitrer
quelles données utiliser se trouve dans une classe de type Repository.
 Le Repository est le seul à pouvoir accéder aux données.
 Il convient de créer un Repository par type de donnée dont l’application a besoin.

Notre couche Données est maintenant développée, et la fonction getQuestions() du


Repository QuizRepository est prête à être appelée depuis le ViewModel associé à l’écran
de quiz. Nous allons maintenant coder la logique métier spécifique à notre fonctionnalité de
quiz dans le ViewModel associé.

XII-Implémentez le ViewModel de l’écran de quiz

Découvrez le pattern ViewModel

Pour rappel, le pattern ViewModel a deux objectifs :

1. Exposer des données, ou états, représentant les informations dont l’interface a besoin.
2. Fournir des fonctions correspondant aux événements qui ont lieu sur l’interface et qui
nécessitent la modification des données.

Nous avons vu qu’un Repository est spécifique au type de donnée qu’il manipule ; par
exemple QuestionRepository permet de gérer les données de type Question . Le
ViewModel quant à lui va être spécifique à une fonctionnalité. Par exemple, nous allons
créer un ViewModel pour la fonctionnalité de quiz de notre application. Nous
l’appellerons QuizViewModel . Si nous avions un autre écran affichant par exemple le
classement des meilleurs scores de l’utilisateur, nous pourrions créer un ViewModel dédié,
que l’on appellerait RankingViewModel par exemple.

La classe ViewModel fournie par Google

Pour enrichir davantage ce pattern qui n’est pas spécifique à Android, Google fournit une
classe, nommée également ViewModel , qui a la capacité de stocker les données qu’elle
contient en tenant compte du cycle de vie de la vue associée. Plus spécifiquement, grâce à

P a g e 81 | 109
Programmation Mobile Android MOUOMENE

cette classe ViewModel, les données stockées dans des variables vont pouvoir survivre aux
modifications de configuration de notre application, telles que les rotations d'écran.

Pour utiliser cette classe, il suffit de l’étendre en déclarant par exemple public class
QuizViewModel extends ViewModel {} .

Créez une classe de type ViewModel

Commençons par organiser correctement notre projet. Il est d’usage de positionner tout le
code relatif à la gestion de l’interface d’une fonctionnalité au même endroit, habituellement
au sein d’un package nommé ui .

Organisez le package ui

Vous pouvez donc créer un package ui de la même manière que vous l’avez fait dans le
chapitre précédent. Ensuite vous pouvez déplacer dans le package ui tout le code de notre
application relatif à la gestion de l’interface utilisateur, c’est-à-dire :

 QuizFragment ;
 WelcomeFragment .

Pour cela, il suffit de faire un drag & drop de ces fichiers dans le package cible.

Vous pouvez aller encore plus loin dans l’organisation de votre projet, en créant deux sous-
packages quiz et welcome au sein du package ui , et en y déplaçant les fichiers relatifs à
ces fonctionnalités. C’est typiquement le genre d’organisation de fichiers que vous pouvez
retrouver au sein d’un projet en entreprise. Lorsqu’une application grossit, avec une bonne
organisation de package, c’est beaucoup plus simple de comprendre la structure du projet et
de naviguer.

Créez la classe QuizViewModel

Maintenant que tout est bien rangé, il est temps de créer cette fameuse classe de
type ViewModel que nous nommerons QuizViewModel , en référence à la fonctionnalité de
quiz qu’elle permet de gérer.

P a g e 82 | 109
Programmation Mobile Android MOUOMENE

Comme une classe de type ViewModel gère la logique relative à une interface, nous pouvons
ranger ce type de classe dans le package ui . Dans notre cas, nous pouvons donc
créer QuizViewModel dans le package ui.quiz .

Organisation des fichiers de notre projet dans différents packages

À vous de jouer !

Vous allez pouvoir implémenter la classe QuizViewModel . Elle étend la


classe ViewModel fournie par Android, et prend en paramètre la
classe QuestionRepository , le fameux médiateur qui nous fournit les données dont nous
avons besoin : la liste de questions.

P a g e 83 | 109
Programmation Mobile Android MOUOMENE

Voici ce que vous devriez obtenir :

public class QuizViewModel extends ViewModel {

private QuestionRepository questionRepository;

public QuizViewModel(QuestionRepository questionRepository) {

this.questionRepository = questionRepository;

Il est maintenant temps de compléter QuizViewModel avec le code relatif à la logique du


quiz. Pour cela, nous allons concrètement devoir identifier les états et les événements dont
notre interface va avoir besoin.

Identifiez les états de l’écran de quiz

À l’échelle de l’écran de quiz de notre application, les états permettant de définir l’affichage
sont :

 la question en cours ;
 savoir si l’utilisateur est arrivé à la dernière question, pour afficher le bouton
“Finish” ;
 le score en particulier pour afficher le score final.

Ces états doivent donc être partagés avec la vue ; dans notre cas, avec le
fragment QuizFragment . Pour les partager, nous allons utiliser le design pattern Observer.

Encore un pattern ? Mais à quoi sert-il ?

Le pattern Observer repose sur le principe suivant : un observable émet des informations.
Un ou des observateurs reçoivent ces informations.

Autrement dit, lorsqu’une variable de type observable est modifiée, elle va notifier ses
observateurs de ce changement. Les observateurs vont pouvoir réagir à ces changements en
les reflétant dans l’interface.
P a g e 84 | 109
Programmation Mobile Android MOUOMENE

Pattern Observer

Pour pouvoir créer une telle variable capable de notifier des observateurs, tout en tenant
compte du cycle de vie particulier d’une application Android, Google fournit les LiveData .

Exposez les états grâce aux LiveData

Un LiveData peut contenir n’importe quel type de donnée : un type primitif comme
un Integer , ou un objet spécifique à notre application, comme Question .

Pour créer un LiveData , il faut utiliser la classe MutableLiveData qui prend en paramètre
une valeur initiale. Par exemple, pour initialiser l’état contenant la question en cours, ajoutez
le code suivant dans la classe QuizViewModel :

P a g e 85 | 109
Programmation Mobile Android MOUOMENE

MutableLiveData<Question> currentQuestion = new MutableLiveData<Question>();

Ainsi déclarée, la valeur initiale contenue dans ce LiveData est nulle, car nous n’avons rien
renseigné en paramètre de l’objet MutableLiveData .

Plus tard, pour mettre à jour la valeur contenue dans ce LiveData , il suffira d’appeler la
fonction postValue() comme ceci :

currentQuestion.postValue(questions.get(1));

Pour lire le contenu d’un LiveData , il suffit d’appeler la fonction getValue() ; par
exemple : currentQuestion.getValue(); .

Pour écouter cet observable depuis QuizFragment , nous verrons dans la partie suivante
qu’il faudra d’écrire quelque chose comme ça :

viewModel.currentQuestion.observe(getViewLifecycleOwner(), new Observer<Question>() {

@Override

public void onChanged(Question question) {

/* on récupère ici les mises à jour via la variable question */

});

Pour le moment, restons dans la classe QuizViewModel . Nous pouvons créer les deux autres
états dont a besoin l’interface, soit :

 un booléen permettant de savoir si l’utilisateur est arrivé à la dernière question,


initialisé à faux ;
 le score initialisé à 0.

Cela donne :

MutableLiveData<Integer> score = new MutableLiveData<Integer>(0);

MutableLiveData<Boolean> isLastQuestion = new MutableLiveData<Boolean>(false);

Les états de QuizViewModel sont maintenant initialisés, et ils n’attendent plus que d’être
modifiés suite aux événements ayant lieu sur l’interface.
P a g e 86 | 109
Programmation Mobile Android MOUOMENE

Identifiez les événements de l’écran de quiz

À l’échelle de l’écran de quiz de notre application, les événements qui vont impacter l’état de
l’interface sont :

 lorsque l’utilisateur arrive pour la première fois sur le quiz, celui-ci démarre ;
 lorsque l’utilisateur clique sur une réponse à une question, pour connaître la validité de
la réponse ;
 lorsque l'utilisateur clique sur “Next” pour passer à la question suivante.

Cela se traduit par la création de trois fonctions dans notre ViewModel :

 startQuiz ;

 isAnswerValid ;

 nextQuestion .

À vous de jouer !

Essayez de coder seul(e) ces trois fonctions. Si besoin, voici quelques pistes pour leur
conception :

 la fonction startQuiz récupère les questions via QuestionRepository , et les


stockent dans une variable globale. Elle initialise également
l’état currentQuestion avec la première question ;
 la fonction isAnswerValid prend en paramètre l’index de la réponse, et retourne un
booléen selon que la réponse est vraie ou fausse. Pour savoir si la réponse est correcte,
elle utilise l’état currentQuestion afin de récupérer l’index de bonne réponse. Si la
réponse est correcte, elle met également à jour l’état correspondant au score ;
 la fonction nextQuestion met à jour l’état currentQuestion avec la question
suivante, ainsi que l’état isLastQuestion dans le cas où l’utilisateur arrive à la
dernière question. Cette fonction nécessite la création d’une variable globale contenant
l’index de la question en cours.

Voici la correction :

public class QuizViewModel extends ViewModel {

P a g e 87 | 109
Programmation Mobile Android MOUOMENE

private QuestionRepository questionRepository;

private List<Question> questions;

private Integer currentQuestionIndex = 0;

public QuizViewModel(QuestionRepository questionRepository) {

this.questionRepository = questionRepository;

MutableLiveData<Question> currentQuestion = new MutableLiveData<Question>();

MutableLiveData<Integer> score = new MutableLiveData<Integer>(0);

MutableLiveData<Boolean> isLastQuestion = new MutableLiveData<Boolean>(false);

public void startQuiz(){

questions = questionRepository.getQuestions();

currentQuestion.postValue(questions.get(0));

public void nextQuestion() {

Integer nextIndex = currentQuestionIndex + 1;

if(nextIndex >= questions.size()) {

return; // should not happened as the 'Next' button is not displayed at the
last question

} else if (nextIndex == questions.size() - 1) {

isLastQuestion.postValue(true);
P a g e 88 | 109
Programmation Mobile Android MOUOMENE

currentQuestionIndex = nextIndex;

currentQuestion.postValue(questions.get(currentQuestionIndex));

public Boolean isAnswerValid(Integer answerIndex) {

Question question = currentQuestion.getValue();

boolean isValid = question != null && question.getAnswerIndex() == answerIndex;

Integer currentScore = score.getValue();

if(currentScore != null && isValid) {

score.setValue(currentScore + 1);

return isValid;

En résumé

 Il est d’usage de positionner tous les fichiers relatifs à l’interface d’une application au
même endroit, par exemple un package nommé ui .
 Le ViewModel contient la logique spécifique à l’affichage d’un écran sous forme
d’états et d’événements.
 Google fournit une classe ViewModel spécifique à Android, qui permet de stocker les
variables qu’elle contient en tenant compte des changements de configuration de la
vue qui la manipule.
 Google fournit les LiveData pour exposer les états à la vue sous forme d’observables.
 On initialise un LiveData via la classe MutableLiveData . Pour lire sa valeur, on
utilise la fonction getValue() ; pour remplacer sa valeur, on utilise la
fonction postValue() .
P a g e 89 | 109
Programmation Mobile Android MOUOMENE

Bravo ! Vous avez parcouru un sacré chemin dans la structuration du code de votre
application. C’est l’heure de la dernière étape, la dynamisation des composants de notre
interface grâce aux états et événements que vous venez de créer dans QuizViewModel.

XIII-Dynamisez l’interface grâce aux données exposées par le

ViewModel

Dans le chapitre précédent, nous avons préparé tous les états et événements permettant à
l’interface d’être dynamisée. En particulier les états suivants :

 currentQuestion permettant de savoir quelle question afficher ;


 isLastQuestion permettant de savoir quand nous devons afficher le bouton “Finish”
à la place du bouton “Next” ;
 score permettant de récupérer le score de l’utilisateur, en particulier à la fin du quiz
pour afficher le score total.

Ainsi que les événements suivants :

 startQuiz pour démarrer le quiz et récupérer en particulier les questions du quiz ;


 nextQuestion , pour passer à la question suivante ;
 isAnswerValid , pour vérifier la validité de la réponse de l’utilisateur.

Pour pouvoir utiliser ces états et ces événements, il nous faut d’abord créer une variable de
type QuizViewModel au sein du fragment QuizFragment .

Référencez QuizViewModel depuis QuizFragment

Pour créer un objet de type QuizViewModel au sein du fragment QuizFragment , nous ne


pouvons pas tout simplement écrire ceci :

private QuizViewModel viewModel = new QuizViewModel(

new QuestionRepository(

new QuestionBank()

P a g e 90 | 109
Programmation Mobile Android MOUOMENE

);

Pourquoi ?

Premièrement parce que dans une classe d’une certaine couche d’architecture, c’est une
mauvaise pratique d’instancier via le mot-clé new une classe provenant d’une autre couche.
Cela réduit la testabilité de chaque couche en isolation. C’est pour cela que nous utilisons
souvent ce qui s’appelle de l’injection de dépendance.

Deuxièmement, parce nous voulons que le ViewModel ait conscience du cycle de vie de la
vue qui l’utilise, en particulier pour survivre aux changements de configuration de celle-ci.
Or, en faisant comme dans le code ci-dessus, le ViewModel sera recréé à chaque changement
de configuration de la vue.

C’est pour cela qu’il convient d’utiliser la classe ViewModelProvider fournie par Google
avec les ViewModel .

Si le ViewModel que l’on souhaite instancier n’a pas de paramètre, alors il suffit d’écrire :

private viewModel = new ViewModelProvider(this).get(QuizViewModel.class);

Mais, malheureusement ça n’est pas notre cas, et ça l’est rarement. En effet, notre
classe QuizViewModel prend en paramètre QuestionRepository . Nous allons donc devoir
fournir une factory au ViewModelProvider . Une factory est simplement un pattern
permettant de déléguer la création d'une classe à une autre .

Oui, je sais, encore un pattern… Mais ne vous prenez pas tellement la tête avec celui-ci dans
ce cours. Les patterns essentiels à comprendre sont les patterns Repository, ViewModel et
Observer. De plus, en continuant votre apprentissage du développement Android, vous aurez
la chance de découvrir des bibliothèques qui vont générer seules ce genre de factory (comme
la bibliothèque Hilt préconisée par Google, ou encore Koin qui est également très populaire).

Créez une Factory

Dans un package injection , au même niveau que les packages data et ui , créez une
classe ViewModelFactory , et copiez-collez le code ci-dessous.

public class ViewModelFactory implements ViewModelProvider.Factory {

private final QuestionRepository questionRepository;

P a g e 91 | 109
Programmation Mobile Android MOUOMENE

private static ViewModelFactory factory;

public static ViewModelFactory getInstance() {

if (factory == null) {

synchronized (ViewModelFactory.class) {

if (factory == null) {

factory = new ViewModelFactory();

return factory;

private ViewModelFactory() {

QuestionBank questionBank = QuestionBank.getInstance();

this.questionRepository = new QuestionRepository(questionBank);

@Override

@NotNull

public <T extends ViewModel> T create(Class<T> modelClass) {

if (modelClass.isAssignableFrom(QuizViewModel.class)) {

return (T) new QuizViewModel(questionRepository);

P a g e 92 | 109
Programmation Mobile Android MOUOMENE

throw new IllegalArgumentException("Unknown ViewModel class");

Je vous explique tout ça ! Nous avons créé une classe ViewModelFactory implémentant
l'interface ViewModelProvider.Factory créée par Google, et qui sera utilisée par la suite
pour déclarer notre ViewModel dans notre fragment. Nous lui définissons ici un constructeur
contenant les objets dont nous avons besoin pour instancier correctement notre
classe QuizViewModel . Cette classe nous permet de regrouper le processus de création de
nos ViewModel dans une Factory dédiée, ViewModelFactory .

Ainsi, si jamais par la suite nous souhaitons créer un autre ViewModel , par
exemple UserViewModel , nous le déclarerons ici, dans cette même Factory , à l'intérieur
de la méthode create() .

À vous de jouer !

Au sein de QuizFragment , vous pouvez maintenant créer une variable globale de


type QuizViewModel . Ensuite, instanciez-la au sein de la fonction onCreate() , et
attention, pas ailleurs.

En effet, c’est ce qui va permettre de créer le ViewModel lors de la première création du


fragment, ensuite de le lier à son cycle de vie via l’implémentation
du ViewModelProvider de Google, et de ne pas le recréer si ça n’est pas nécessaire.

Vous devriez au final avoir ajouté ces lignes de code :

private QuizViewModel viewModel;

@Override

public void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

viewModel = new ViewModelProvider(this,


ViewModelFactory.getInstance()).get(QuizViewModel.class);

P a g e 93 | 109
Programmation Mobile Android MOUOMENE

Il est maintenant temps de dynamiser les composants de notre interface.

Affichez des questions et des propositions

Rappelez-vous, au sein du layout quiz_fragment.xml , nous avons dans le chapitre 2 créé


les composants avec les identifiants suivants :

 question pour le composant de type TextView pour contenir la question ;


 answer1 , answer2 , answer3 et answer4 , les boutons pour afficher les réponses
possibles.

Nous avions également configuré la fonctionnalité de ViewBinding , pour pouvoir accéder à


ces composants depuis le code Java en faisant
simplement binding.question , binding.answer1 , etc.

Il ne vous reste plus qu’à


dynamiser question , answer1 , answer2 , answer3 et answer4 pour afficher la question
en cours accessible via l’état de type LiveData currentQuestion .

Pour écouter les modifications d’un LiveData , il faut appeler la


fonction observe du LiveData en question, comme ceci :

viewModel.currentQuestion.observe(getViewLifecycleOwner(), new Observer<Question>() {

@Override

public void onChanged(Question question) {

// TODO à développer

});

Cette fonction prend deux paramètres en entrée :

 le premier permet au LiveData d’avoir connaissance du cycle de vie de la vue, pour


que celui-ci notifie la vue de ces changements, uniquement quand la vue est active ;

P a g e 94 | 109
Programmation Mobile Android MOUOMENE

 le second paramètre permet de déclarer un objet de type Observer , qui contient en


particulier une fonction onChanged() qui sera appelée lorsqu’une nouvelle valeur
sera émise dans le LiveData .

À vous de jouer !

Vous savez maintenant tout ce qu’il faut pour dynamiser le contenu des composants
permettant d’afficher une question et ses propositions. Essayez par vous-même avant de jeter
un coup d'œil à la correction ci-dessous.

@Override

public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

viewModel.startQuiz();

viewModel.currentQuestion.observe(getViewLifecycleOwner(), new Observer<Question>() {

@Override

public void onChanged(Question question) {

updateQuestion(question);

});

private void updateQuestion(Question question) {

binding.question.setText(question.getQuestion());

binding.answer1.setText(question.getChoiceList().get(0));

binding.answer2.setText(question.getChoiceList().get(1));

binding.answer3.setText(question.getChoiceList().get(2));

P a g e 95 | 109
Programmation Mobile Android MOUOMENE

binding.answer4.setText(question.getChoiceList().get(3));

Quelques remarques sur ce code :


 Le code est positionné dans la fonction onViewCreated . C’est très spécifique au
Fragment et au fait que le lien avec la vue XML est fait dans la
fonction onCreateView et pas dans la fonction onCreate . Si nous avions positionné
notre code dans la fonction onCreate() , la vue n’étant pas encore créée, nous
aurions une exception telle que IllegalStateException: Can't access the
Fragment View's LifecycleOwner when getView() is null i.e., before

onCreateView() or after onDestroyView().

 Il ne faut pas oublier d’appeler la fonction startQuiz() de notre ViewModel .


 La fonction updateQuestion se charge de remplacer les textes des composants de
l’interface via la fonction setText .

À ce stade, vous pouvez lancer l’application sur un appareil, et normalement après avoir
cliqué sur le bouton “Let’s play” de l’écran d’accueil, vous devriez voir apparaître la première
question avec ces propositions. Voilà qui est plutôt satisfaisant, non ?

Récupérez la réponse de l’utilisateur

Maintenant, pour gérer les réponses de l’utilisateur aux questions du quiz, il va falloir :

 récupérer l’action de clic de l’utilisateur sur les quatre réponses possibles grâce à la
fonction setOnClickListener (souvenez- vous, nous l’avons déjà utilisée dans la
partie 2 – chapitre 4) ;
 vérifier si la réponse est correcte ou non. Mettre à jour en conséquence l’interface,
c’est-à-dire :
o changer la couleur du bouton en vert ou en rouge,
o afficher le composant de type TextView, validityText , présent dans notre
layout avec le texte “Good Answer” ou “Bad Answer” ;
 puis afficher le bouton Next qui permet soit de passer à la question suivante, soit de
terminer le quiz s’il s’agit de la dernière question.

Allez-y, essayez de produire le code correspondant à ces spécifications. On se retrouve ci-


dessous pour voir mon implémentation.

Dans la fonction onViewCreated , ajoutez le code suivant :


P a g e 96 | 109
Programmation Mobile Android MOUOMENE

binding.answer1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

updateAnswer(binding.answer1, 0);

});

binding.answer2.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

updateAnswer(binding.answer2, 1);

});

binding.answer3.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

updateAnswer(binding.answer3, 2);

});

binding.answer4.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

P a g e 97 | 109
Programmation Mobile Android MOUOMENE

updateAnswer(binding.answer4, 3);

});

Comme vous pouvez le voir, ce code permet de détecter le clic sur chaque bouton. Pour que
ce code fonctionne, il manque la fonction updateAnswer , que vous pouvez ajouter au sein
de la classe QuizFragment :

private void updateAnswer(Button button, Integer index){

showAnswerValidity(button, index);

enableAllAnswers(false);

binding.next.setVisibility(View.VISIBLE);

private void showAnswerValidity(Button button, Integer index){

Boolean isValid = viewModel.isAnswerValid(index);

if (isValid) {

button.setBackgroundColor(Color.parseColor("#388e3c")); // dark green

binding.validityText.setText("Good Answer ! 💪");

} else {

button.setBackgroundColor(Color.RED);

binding.validityText.setText("Bad answer 😢");

binding.validityText.setVisibility(View.VISIBLE);

P a g e 98 | 109
Programmation Mobile Android MOUOMENE

private void enableAllAnswers(Boolean enable){

List<Button> allAnswers = Arrays.asList(binding.answer1, binding.answer2,


binding.answer3, binding.answer4);

allAnswers.forEach( answer -> {

answer.setEnabled(enable);

});

Démarrez l’application sur un appareil, et là, magie, lorsque vous cliquez sur une proposition,
l’interface se met à jour comme il faut.

P a g e 99 | 109
Programmation Mobile Android MOUOMENE

Capture d’écran de l’application en cours de construction, lorsque l’utilisateur clique sur une bonne
réponse.

Un point sur l’accessibilité de cette fonctionnalité

P a g e 100 | 109
Programmation Mobile Android MOUOMENE

Même si d’apparence à ce stade du développement du quiz, la fonctionnalité semble marcher


comme il le faut, prenons le temps d’analyser si celle-ci est bien accessible. Les contrastes
sont corrects sur cet écran, les boutons suffisamment gros, et l’information de succès ou
d’échec n’est véhiculée que par de la couleur grâce au texte validityText . Cependant, nous
avons oublié de considérer les personnes aveugles qui utilisent un lecteur d’écran pour
naviguer sur l’interface.

En effet, pour naviguer sur un téléphone, une personne aveugle utilise Talkback, un outil
d’assistance installé sur tous les téléphones Android. Pour parcourir un écran, Talkback va
tout d’abord positionner le focus sur l’élément le plus haut à gauche de l’écran, et le vocaliser.
La personne va ensuite pouvoir déplacer le focus sur chaque élément de l’interface de son
choix, en interagissant sur l’écran. Talkback se chargera à nouveau de vocaliser à quoi
correspond l’élément, et comment interagir avec.

Dans le cas de notre quiz, lorsque le lecteur d’écran est positionné sur une réponse, et que
l’utilisateur effectue un clic sur cette réponse, si nous ne faisons rien et que l’utilisateur ne
déplace pas le focus jusqu’à l’élément textValidity en bas de l’écran, il ne sait pas s’il a
bien répondu ou non. Pour régler ce souci, il convient d’utiliser la
fonction announceForAccessibility , accessible au sein de la classe View . Elle permet en
effet de forcer l’annonce d’un message par le lecteur d’écran. Bien sûr, ce message est
vocalisé uniquement si l’utilisateur utilise Talkback. Cela donne dans notre cas :

private void showAnswerValidity ( Button button , Integer index ){


Boolean isValid = viewModel .isAnswerValid( index );
if ( isValid) {
button .setBackgroundColor( Color .parseColor( "#388e3c")); // dark green
binding .validityText .setText ("Good Answer ! 💪" );
button .announceForAccessibility( "Good Answer !" );
} else {
button .setBackgroundColor (Color.RED );
binding .validityText .setText ("Bad answer 😢" );
button .announceForAccessibility( "Bad answer" );
}
binding .validityText.setVisibility (View .VISIBLE );
}
Nous pouvons utiliser la fonction announceForAccessibility depuis le type Button ,
car Button hérite de View et peut donc utiliser la
fonction announceForAccessibility présente dans cette classe.

P a g e 101 | 109
Programmation Mobile Android MOUOMENE

N’hésitez pas à vous lancer dans un tutoriel pour apprendre à utiliser Talkback, puis lancez
SuperQuiz sur un appareil physique, afin de valider que votre code fonctionne bien avec
Talkback.

Ça fait du bien de parler un peu d’accessibilité, non ? Pensez-y, une personne sur cinq est en
situation de handicap. Ça n’est pas pour autant qu’elle doit se priver d’utiliser les outils du
numérique, aujourd’hui encore trop peu accessibles.

Passez à la suite

Maintenant, nous devons permettre à l’utilisateur de passer à la suite une fois qu’il a répondu
à une question. Cela revient à dynamiser le bouton avec l’identifiant next . Il va falloir en
particulier :

 observer l’état isLastQuestion afin de conditionner le texte contenu dans le


bouton next : “Finish” ou “Next” ;
 passer à la question suivante lorsque l’utilisateur clique sur “Next” ;
 afficher son score final dans une fenêtre de type Dialog si l’utilisateur clique sur
“Finish”.

À nouveau, essayez par vous-même avant de regarder ma version du code ci-dessous.

Voici la correction.

Pour observer l’état isLastQuestion , j’ai ajouté le code suivant au sein de la


fonction onViewCreated du fragment du quiz :

viewModel.isLastQuestion .observe( getViewLifecycleOwner (), new Observer<Boolean> () {


@Override
public void onChanged( Boolean isLastQuestion ) {
if ( isLastQuestion ){
binding .next.setText( "Finish" );
} else {
binding .next.setText( "Next");
}
}
});

Pour gérer l’action de clic sur le bouton next , j’ai également ajouté le code suivant dans la
fonction onViewCreated :

P a g e 102 | 109
Programmation Mobile Android MOUOMENE

binding.next.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

Boolean isLastQuestion = viewModel.isLastQuestion.getValue();

if(isLastQuestion != null && isLastQuestion){

displayResultDialog();

} else {

viewModel.nextQuestion();

resetQuestion();

});

Le code précédent utilise les deux fonctions suivantes, pour réinitialiser l’état de l’interface
lorsque l’on passe à une autre question :

private void resetQuestion (){


List<Button> allAnswers = Arrays .asList (binding .answer1 , binding .answer2 , binding .answer3 ,
binding .answer4);
allAnswers.forEach( answer -> {
answer .setBackgroundColor (Color.parse (“#6200EE ”));
});
binding .validityText.setVisibility (View .INVISIBLE );
enableAllAnswers (true );
}
private void enableAllAnswers (Boolean enable){
List<Button> allAnswers = Arrays .asList (binding .answer1 , binding .answer2 , binding .answer3 ,
binding .answer4);
allAnswers.forEach( answer -> {
answer .setEnabled (enable );
});
}

Quelques remarques concernant ces quelques lignes de code :

P a g e 103 | 109
Programmation Mobile Android MOUOMENE

La fonction resetQuestion est utilisée pour réinitialiser l’état des boutons, pour les préparer
pour la prochaine question. Dans le code de cette fonction, nous créons une liste avec tous les
boutons de l’interface. Puis via la lambda forEach() , nous changeons la couleur de fond.
Nous masquons le texte validityText et nous réactivons le clic sur tous les boutons via la
fonction enableAllAnswers() .

Pour créer et afficher une fenêtre de dialogue lorsque l’utilisateur clique sur Finish , nous
allons utiliser la classe Dialog d’Android. En particulier via le
builder AlertDialog.Builder . Il permet de créer une popup avec un style par défaut, sans
avoir besoin de définir de design via un layout XML.

Exemple de fenêtre de dialogue construite avec AlertDialog.Builder

C’est ce que nous faisons dans la fonction displayResultDialog :

private void displayResultDialog () {


AlertDialog .Builder builder = new AlertDialog.Builder( getActivity());

builder .setTitle ("Finished !" );


Integer score = viewModel.score.getValue ();
builder .setMessage( "Your final score is "+ score );
builder .setPositiveButton( "Quit", new DialogInterface .OnClickListener() {
public void onClick (DialogInterface dialog , int id ) {
goToWelcomeFragment();
}
});
AlertDialog dialog = builder.create ();

P a g e 104 | 109
Programmation Mobile Android MOUOMENE

dialog .show ();


}

Dans ce code, après avoir instancié un objet de type AlertDialog.Builder , nous


configurons :

 le titre de la fenêtre via la fonction setTitle de la classe AlertDialog.Builder ;


 un message via la fonction setMessage qui sera contenu au centre de cette fenêtre ;
 un bouton avec une allure “positive” et l’action associée au clic sur ce bouton. Ici nous
appelons la fonction goToWelcomeFragment que nous détaillerons ensuite.

Puis, nous créons un objet de type Dialog grâce au builder précédent en appelant sa
fonction create . Enfin pour afficher la fenêtre, il faut appeler la fonction show issue de la
classe Dialog .

Pour naviguer vers l’écran d’accueil de notre application une fois que l’utilisateur a décidé de
fermer la fenêtre de dialogue précédente, nous allons à nouveau utiliser la
classe FragmentManager de la même manière que nous l’avions fait pour afficher le
fragment QuizFragment . Cela donne :

private void goToWelcomeFragment(){

WelcomeFragment welcomeFragment = WelcomeFragment.newInstance();

FragmentManager fragmentManager = getParentFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager

.beginTransaction();

fragmentTransaction.replace(R.id.container, welcomeFragment).commit();

Votre quiz est maintenant opérationnel. Lancez l’application sur un appareil et amusez-vous.

Récapitulons en vidéo

Retrouvez ces différentes étapes dans la vidéo ci-dessous :

Pour voir tout le code, vous pouvez vous référer à la branche de la partie 3 sur GitHub.

P a g e 105 | 109
Programmation Mobile Android MOUOMENE

En résumé

 Pour utiliser un ViewModel depuis une vue, il faut l’instancier via la


classe ViewModelProvider fournie par Google.
 Pour observer un LiveData et réagir à ses changements de valeur, il faut appeler la
fonction observe() .
 L’accessibilité ne s’arrête pas à veiller aux contrastes et à la taille des boutons. Les
personnes aveugles utilisent par exemple Talkback pour lire les éléments à l’écran.
 La fonction announceForAccessibility permet d’annoncer un message uniquement
pour un lecteur d’écran. Il est utile de l’utiliser pour annoncer un changement visuel
qui a lieu sur l’interface.
 La classe Dialog permet d’afficher une popup. Pour créer une instance
de Dialog avec un style par défaut, nous pouvons utiliser AlertDialog.Builder .

Vous avez parcouru un sacré chemin en construisant votre première application Android.
Bien joué. Mais ne partez pas tout de suite ; dans la prochaine partie, je vous donne des
pistes pour faire évoluer votre application.

XIV-Découvrez des pistes d’évolution

Votre application est maintenant dynamique et ressemble à un quiz. Bien joué ! Mais soyons
honnêtes, elle nécessite quelques améliorations avant de finir sur le Play Store. Vous avez
sûrement déjà des idées en tête. Voici les miennes, avec pour chaque idée quelques pistes
techniques :

Récupérez les questions d’une API tierce

Pour cela, vous pourrez par exemple utiliser Retrofit qui permet d’effectuer des requêtes
HTTP plus facilement.

Si vos données externes sont sur Firebase (par exemple sur Firestore), utilisez directement
la bibliothèque Firebase disponible pour Android. Firebase est une très bonne solution pour
développer sans trop d’efforts un backend, et elle est très populaire.

Dans tous les cas, comme nous sommes sur une fonctionnalité liée à la manipulation des
données, vous l’aurez deviné, le code que vous écrirez sera dans la couche Données.
P a g e 106 | 109
Programmation Mobile Android MOUOMENE

Le Repository QuestionRepository se chargera à nouveau de faire le médiateur pour


accéder aux données, comme le montre le schéma ci-dessous.

API au sein de la couche Données dont les données sont arbitrées par le QuestionRepository

Créez plus de questions

P a g e 107 | 109
Programmation Mobile Android MOUOMENE

Aujourd’hui, SuperQuiz, c’est cinq questions seulement. Un peu léger, non ?

Sur ce point, vous pouvez imaginer créer plus de questions :

 soit en local, de la même manière que nous l’avons fait, en ajoutant simplement des
questions dans QuestionBank et en générant à chaque lancement de quiz 5 questions
choisies aléatoirement dans cette liste ;
 soit sur une base de données externe que vous récupérez via l’API, comme vu dans le
point précédent, qui se charge de vous envoyer 5 questions aléatoires ;
 vous pouvez même aller encore plus loin en profitant au maximum du rôle de
médiateur du Repository, en implémentant une logique qui récupère des questions
d’une API et les stocke en local dans une vraie base de données, en utilisant Room,
par exemple. Ces questions en local peuvent être utilisées plus tard pour éviter
d’effectuer un appel API à nouveau.

Bref, ce ne sont que quelques idées pour offrir plus de questions à vos utilisateurs. Dans tous
les cas, on se trouve côté code à nouveau dans la couche Données.

Personnalisez l’expérience de l’utilisateur

À l’heure actuelle, dans notre application, nous récupérons le nom de l’utilisateur dans un
champ de saisie sur le premier écran, mais à part vérifier que l’utilisateur saisit quelque chose
pour démarrer le quiz, nous ne nous servons pas de sa saisie. Un peu frustrant, non ?

Vous pouvez imaginer plein de fonctionnalités tirant profit de cette saisie. En voici quelques-
unes :

 En sauvegardant le nom de l’utilisateur en local, vous pouvez personnaliser son


accueil dans l’application et l’annonce de son score.
o Pour sauvegarder ce genre de données simples, rien de tel que
les SharedPreferences.
 En sauvegardant le meilleur score de l’utilisateur également dans les
SharedPreferences, vous pouvez lui rappeler ce score dans l’interface afin de le
mettre au défi.
 En allant encore plus loin, on peut imaginer sauvegarder sur un service externe les
scores de tous les utilisateurs de l’application, pour pouvoir afficher dans un
nouveau fragment le classement général de tous les utilisateurs de l’application.
P a g e 108 | 109
Programmation Mobile Android MOUOMENE

Déployer l’application sur le Play Store

Imaginons maintenant que vous ayez développé une application SuperQuiz au top, et que
vous souhaitiez la déployer sur le Play Store.

Pour cela, vous pouvez suivre ce tutoriel. Toutefois, sachez que pour pouvoir déployer une
application sur le Play Store, il faut disposer d’un compte Google Play Developer, et que
cela coûte 15 $.

Si vous avez simplement besoin de déployer votre application sur quelques appareils, par
exemple dans votre entourage, sans passer par le Play Store, Firebase Distribution vous
permet de partager gratuitement votre application.

En résumé

Vous voilà avec plein d’idées en tête pour faire évoluer cette application. Vous disposez en
plus des bases nécessaires pour structurer cette application afin de lui permettre de grandir
sereinement. Vous avez franchi une étape importante qui, j’en suis sûre, vous permettra
d’évoluer avec de bonnes bases dans le monde du développement d'applications Android.
Bravo à vous !

P a g e 109 | 109

Vous aimerez peut-être aussi