Projet Android
Projet Android
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.
Créez le projet
P a g e 1 | 109
Programmation Mobile Android MOUOMENE
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
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
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
P a g e 6 | 109
Programmation Mobile Android MOUOMENE
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
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 :
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
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
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 :
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.
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 :
P a g e 15 | 109
Programmation Mobile Android MOUOMENE
</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 :
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).
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
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.
P a g e 19 | 109
Programmation Mobile Android MOUOMENE
P a g e 20 | 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 !
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).
P a g e 23 | 109
Programmation Mobile Android MOUOMENE
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é.
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
P a g e 25 | 109
Programmation Mobile Android MOUOMENE
P a g e 26 | 109
Programmation Mobile Android MOUOMENE
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.
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 :
</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.
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
<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
P a g e 30 | 109
Programmation Mobile Android MOUOMENE
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
Le padding consiste à ajouter de l'espace entre le contenu d'un élément et les bords
de cet élément.
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
P a g e 33 | 109
Programmation Mobile Android MOUOMENE
<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>
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
<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
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.
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 :
P a g e 37 | 109
Programmation Mobile Android MOUOMENE
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.
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.
P a g e 39 | 109
Programmation Mobile Android MOUOMENE
<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 {
/*...*/
}
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
@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.
}
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.
Les deux actions les plus importantes à implémenter sur l’écran d’accueil sont les
suivantes :
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 !
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.
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
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.
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
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.
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
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.
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 :
P a g e 48 | 109
Programmation Mobile Android MOUOMENE
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
Ensuite, dès que l’utilisateur aura sélectionné une réponse, les éléments suivants apparaîtront :
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 ?
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 :
<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.
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: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.
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.
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 !
P a g e 53 | 109
Programmation Mobile Android MOUOMENE
C'est bien beau tout ça, mais comment afficher ce nouveau fragment ?
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 :
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
Récapitulons en vidéo
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 !
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.
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é
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()
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.
Partie 3
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é.
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.
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.
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 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.
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).
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.
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.
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é
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é
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 :
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.
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 :
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
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”.
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
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 :
Pensez bien à importer la classe Java List comme ceci : import java.util.List; .
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
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.
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.
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
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 ?
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
P a g e 78 | 109
Programmation Mobile Android MOUOMENE
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 !
Enfin, faites en sorte que cette classe expose une fonction getQuestions() qui retourne la
liste des questions issues de la banque de questions.
this.questionBank = questionBank;
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.
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.
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 {} .
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.
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 .
À vous de jouer !
P a g e 83 | 109
Programmation Mobile Android MOUOMENE
this.questionRepository = questionRepository;
À 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.
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 .
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
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 :
@Override
});
Pour le moment, restons dans la classe QuizViewModel . Nous pouvons créer les deux autres
états dont a besoin l’interface, soit :
Cela donne :
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
À 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.
startQuiz ;
isAnswerValid ;
nextQuestion .
À vous de jouer !
Essayez de coder seul(e) ces trois fonctions. Si besoin, voici quelques pistes pour leur
conception :
Voici la correction :
P a g e 87 | 109
Programmation Mobile Android MOUOMENE
this.questionRepository = questionRepository;
questions = questionRepository.getQuestions();
currentQuestion.postValue(questions.get(0));
return; // should not happened as the 'Next' button is not displayed at the
last question
isLastQuestion.postValue(true);
P a g e 88 | 109
Programmation Mobile Android MOUOMENE
currentQuestionIndex = nextIndex;
currentQuestion.postValue(questions.get(currentQuestionIndex));
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.
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 :
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 .
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 :
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).
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.
P a g e 91 | 109
Programmation Mobile Android MOUOMENE
if (factory == null) {
synchronized (ViewModelFactory.class) {
if (factory == null) {
return factory;
private ViewModelFactory() {
@Override
@NotNull
if (modelClass.isAssignableFrom(QuizViewModel.class)) {
P a g e 92 | 109
Programmation Mobile Android MOUOMENE
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 !
@Override
super.onCreate(savedInstanceState);
P a g e 93 | 109
Programmation Mobile Android MOUOMENE
@Override
// TODO à développer
});
P a g e 94 | 109
Programmation Mobile Android MOUOMENE
À 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
super.onViewCreated(view, savedInstanceState);
viewModel.startQuiz();
@Override
updateQuestion(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));
À 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 ?
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.
binding.answer1.setOnClickListener(new View.OnClickListener() {
@Override
updateAnswer(binding.answer1, 0);
});
binding.answer2.setOnClickListener(new View.OnClickListener() {
@Override
updateAnswer(binding.answer2, 1);
});
binding.answer3.setOnClickListener(new View.OnClickListener() {
@Override
updateAnswer(binding.answer3, 2);
});
binding.answer4.setOnClickListener(new View.OnClickListener() {
@Override
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 :
showAnswerValidity(button, index);
enableAllAnswers(false);
binding.next.setVisibility(View.VISIBLE);
if (isValid) {
} else {
button.setBackgroundColor(Color.RED);
binding.validityText.setVisibility(View.VISIBLE);
P a g e 98 | 109
Programmation Mobile Android MOUOMENE
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.
P a g e 100 | 109
Programmation Mobile Android MOUOMENE
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 :
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 :
Voici la correction.
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
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 :
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.
P a g e 104 | 109
Programmation Mobile Android MOUOMENE
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 :
.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
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é
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.
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 :
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
API au sein de la couche Données dont les données sont arbitrées par le QuestionRepository
P a g e 107 | 109
Programmation Mobile Android MOUOMENE
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.
À 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 :
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