Poly
Poly
Poly
février-mars 2021
Abstract
Il s’agit des transparents du cours mis sous une forme plus facilement imprimable et lisible.
Ces documents ne sont pas totalement libres de droits. Ce sont des supports de cours
mis à votre disposition pour vos études sous la licence Creative Commons Attribution - Pas
d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.
1 Environnement de développement 17
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.1.1 Qu’est-ce qu’Android ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.1.2 Historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.1.3 Remarque sur les versions d’API . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.1.4 Distribution des versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.1.5 Remarques diverses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.1.6 Programmation d’applications . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.1.7 Applications natives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.1.8 Kotlin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.1.9 Exemple : objet pouvant être null . . . . . . . . . . . . . . . . . . . . . . . . 20
1.1.10 Pas de Kotlin pour ce cours . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2 SDK Android et Android Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.1 SDK et Android Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.2 Android Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
2
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
1.6.1 Paquet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
1.6.2 Signature d’une application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.6.3 Création du keystore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.6.4 Création d’une clé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.6.5 Création du paquet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.6.6 Et voilà . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
4
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
5
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
4 Application liste 73
4.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.1.1 Principe général . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.1.2 Schéma global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.1.3 Une classe pour représenter les items . . . . . . . . . . . . . . . . . . . . . . 74
4.1.4 Données initiales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.1.5 Copie dans un ArrayList . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.1.6 Rappels sur le container List<type> . . . . . . . . . . . . . . . . . . . . . . 75
4.1.7 Données initiales dans les ressources . . . . . . . . . . . . . . . . . . . . . . . 76
4.1.8 Remarques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.2 Affichage de la liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.2.1 Activité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.2.2 Mise en place du layout d’activité . . . . . . . . . . . . . . . . . . . . . . . . 77
4.3 Adaptateurs et ViewHolders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.3.1 Relations entre la vue et les données . . . . . . . . . . . . . . . . . . . . . . . 78
4.3.2 Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.3.3 Recyclage des vues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.3.4 ViewHolders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.3.5 Exemple de ViewHolder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.3.6 Rôle d’un adaptateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.3.7 Structure d’un adaptateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.3.8 Constructeur d’un adaptateur . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.3.9 Méthodes à ajouter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4.4 Configuration de l’affichage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.4.1 Optimisation du défilement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.4.2 LayoutManager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.4.3 LayoutManager dans le layout.xml . . . . . . . . . . . . . . . . . . . . . . . . 82
4.4.4 Disposition en tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.4.5 Disposition en blocs empilés . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.4.6 Séparateur entre items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.5 Actions sur la liste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.5.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.5.2 Modification des données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
4.5.3 Défilement vers un élément . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
5 Ergonomie 88
5.1 Barre d’action et menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.1.1 Barre d’action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.1.2 Réalisation d’un menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.1.3 Spécification d’un menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.1.4 Icônes pour les menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.1.5 Écouteur pour afficher le menu . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.1.6 Réactions aux sélections d’items . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.1.7 Menus en cascade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.2 Menus contextuels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.2.1 Menus contextuels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.2.2 View Holder écouteur de menu . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.2.3 Écouteur dans l’activité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5.3 Annonces et dialogues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.3.1 Annonces : toasts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.3.2 Dialogues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
5.3.3 Dialogue d’alerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.3.4 Boutons et affichage d’un dialogue d’alerte . . . . . . . . . . . . . . . . . . . 94
5.3.5 Dialogues personnalisés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.3.6 Création d’un dialogue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
5.3.7 Affichage du dialogue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.4 Fragments et activités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.4.1 Fragments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.4.2 Tablettes, smartphones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.4.3 Différents types de fragments . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.4.4 Cycle de vie des fragments . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.4.5 Structure d’un fragment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.4.6 Menus de fragments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
5.4.7 Intégrer un fragment dans une activité . . . . . . . . . . . . . . . . . . . . . 98
7
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
6 Realm 108
6.1 Plugin Lombok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
6.1.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
6.1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
8
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
9
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
10
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
11
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
12
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
9 Capteurs 157
9.1 Réalité augmentée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.1.1 Définition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.1.2 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.1.3 Principes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.1.4 Réalité augmentée dans Android . . . . . . . . . . . . . . . . . . . . . . . . . 158
9.2 Permissions Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
9.2.1 Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
9.2.2 Permissions dans le manifeste . . . . . . . . . . . . . . . . . . . . . . . . . . 158
9.2.3 Raffinement de certaines permissions . . . . . . . . . . . . . . . . . . . . . . 159
9.2.4 Demandes de permissions à la volée . . . . . . . . . . . . . . . . . . . . . . . 159
9.2.5 Test d’une autorisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
9.2.6 Demande d’une autorisation . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
9.2.7 Préférences d’application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
9.2.8 Dialogue de demande de droits . . . . . . . . . . . . . . . . . . . . . . . . . . 160
9.2.9 Affichage du dialogue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.2.10 Justification des demandes de droits . . . . . . . . . . . . . . . . . . . . . . . 161
9.3 Capteurs de position . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.3.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.3.2 Utilisation dans Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
9.3.3 Récupération de la position . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
9.3.4 Abonnement aux changements de position . . . . . . . . . . . . . . . . . . . 163
13
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
14
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
15
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
16
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 1
Environnement de développement
1.1. Introduction
1.1.1. Qu’est-ce qu’Android ?
Android est une surcouche au dessus d’un système Linux : Voir la figure 2, page 18.
1.1.2. Historique
• Né en 2004, racheté par Google en 2005, version 1.5 publiée en 2007
• De nombreuses versions depuis. On en est à la version 11 (sept. 2020) et l’API 30. La version 11
est le numéro pour le grand public, et les versions d’API sont pour les développeurs. Exemples :
– 4.1 JellyBean = API 16,
– 6.x Marshmallow = API 23,
– 9.x Pie = API 28
Une API (Application Programming Interface) est un ensemble de bibliothèques de classes pour
programmer des applications. Son numéro de version donne un indice de ses possibilités.
17
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
18
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
19
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
1.1.8. Kotlin
C’est un langage de programmation « symbiotique » de Java :
• une classe Kotlin est compilée dans le même code machine que Java,
• une classe Kotlin peut utiliser les classes Java et réciproquement.
• On peut mélanger des sources Java et Kotlin dans une même application.
Kotlin est promu par Google parce qu’il permet de développer des programmes plus sains. Par
exemple, Kotlin oblige à vérifier chaque appel de méthode sur des variables objets pouvant valoir
null, ce qui évite les NullPointerException.
20
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
String getNomComplet(Personne p) {
return p.getPrenom()+" "+p.getNom();
}
En Kotlin :
En Java amélioré :
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
NB: les import dépendent des bibliothèques utilisées, ici c’est androidx comme en TP.
Cependant, il faut y penser. Kotlin vérifie systématiquement de nombreuses choses (initialisations,
etc.).
21
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
22
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
23
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
24
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
choisir.
25
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
26
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
27
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Figure 8: Éléments28
d’un projet Android
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
29
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
1.3.12. Gradle
Gradle est un outil de construction de projets comme Make (projets C++ sur Unix), Ant (projets
Java dans Eclipse) et Maven.
De même que make se sert d’un fichier Makefile, Gradle se sert de fichiers nommés build.gradle
pour construire le projet.
C’est assez compliqué car AndroidStudio fait une distinction entre le projet global et l’application.
Donc il y a deux build.gradle :
• un script build.gradle dans le dossier racine du projet. Il indique quelles sont les dépendances
générales (noms des dépôts Maven contenant les librairies utilisées).
• un dossier app contenant l’application du projet.
• un script build.gradle dans le dossier app pour compiler l’application.
.
+-- app/
| +-- build/ FICHIERS COMPILÉS
| +-- build.gradle SPÉCIF. COMPILATION
| `-- src/
| +-- androidTest/ TESTS UNITAIRES ANDROID
| +-- main/
| | +-- AndroidManifest.xml DESCR. DE L'APPLICATION
| | +-- java/ SOURCES
| | `-- res/ RESSOURCES (ICONES...)
| `-- test/ TESTS UNITAIRES JUNIT
+-- build/ FICHIERS TEMPORAIRES
+-- build.gradle SPÉCIF. PROJET
`-- gradle/ FICHIERS DE GRADLE
30
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
dependencies {
// support
implementation 'androidx.appcompat:appcompat:1.2.0'
// annotations
annotationProcessor 'org.projectlombok:lombok:1.18.16'
implementation 'androidx.annotation:annotation:1.1.0'
// fuites mémoire
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
}
buildscript {
repositories { ... }
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'io.realm:realm-gradle-plugin:7.0.8' // <-!
}
}
Android Studio affiche un avertissement s’il y a une mise à jour sur les serveurs (repositories). Il faut
alors éditer les numéros de version manuellement, puis reconstruire le projet (sync now ou try again).
On doit parfois compléter le build.gradle du dossier app.
31
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
32
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
33
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Ils sont émis par les applications : debug, infos, erreurs. . . comme syslog sur Unix : date, heure,
gravité, source (code de l’émetteur) et message.
void maMethode() {
Log.i(TAG, "appel de maMethode()");
Fonctions Log.* :
34
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Chaque tablette (device) possède un identifiant, ex: c1608df1b170d4f ou emulator-5554 qu’il faut
fournir aux commandes adb à l’aide de l’option -s.
Par défaut, c’est la seule tablette active qui est concernée.
• Connexion à un shell
36
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
37
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
38
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
39
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
1.6.6. Et voilà
C’est fini pour cette semaine, rendez-vous la semaine prochaine pour un cours sur les interfaces
Android.
40
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 2
41
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
C’est l’appel setContentView(...) qui met en place l’interface. Son paramètre est un identifiant de
ressource, càd d’une disposition de vues d’interface. C’est ce qu’on va étudier maintenant.
2.2. Ressources
2.2.1. Définition
Les ressources sont tout ce qui n’est pas programme dans une application. Dans Android, ce sont les
textes, messages, icones, images, sons, interfaces, styles, etc.
C’est une bonne séparation, car cela permet d’adapter une application facilement pour tous les pays,
cultures et langues. On n’a pas à bidouiller dans le code source et recompiler chaque fois. C’est le
même code compilé, mais avec des ressources spécifiques.
Le programmeur prévoit simplement des variantes linguistiques des ressources qu’il souhaite permettre
de traduire. Ce sont des sous-dossier, ex: values-fr, values-en, values-jp, etc et il n’y a qu’à
modifier des fichiers XML.
42
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Cet identifiant est un entier qui est généré automatiquement par le SDK Android. Comme il va y
avoir de très nombreux identifiants dans une application :
• chaque vue possède un identifiant (si on veut)
• chaque image, icone possède un identifiant
• chaque texte, message possède un identifiant
• chaque style, theme, etc. etc.
Ils ont tous été regroupés dans une classe spéciale appelée R.
2.2.4. La classe R
Cette classe R est générée automatiquement (dans le dossier generated) par ce que vous mettez dans
le dossier res : interfaces, menus, images, chaînes. . . Certaines de ces ressources sont des fichiers
XML, d’autres sont des images PNG.
Par exemple, le fichier res/values/strings.xml :
43
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Rappel : dans la norme XML, le namespace par défaut n’est jamais appliqué aux attributs, donc il
faut mettre le préfixe sur ceux qui sont concernés. Voir le cours XML.
<menu xmlns:android=
"http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:showAsAction="never"
android:title="Configuration"/>
</menu>
44
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Le système android ira chercher automatiquement le bon texte en fonction des paramètres linguistiques
configurés par l’utilisateur.
getResources() est une méthode de la classe Activity (héritée de la classe abstraite Context) qui
retourne une représentation de toutes les ressources du dossier res. Chacune de ces ressources, selon
son type, peut être récupérée avec son identifiant.
<RelativeLayout>
<TextView android:text="@string/bonjour" />
<Button android:text="Commencer" />
</RelativeLayout>
45
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<ImageView
android:src="@drawable/velo"
android:contentDescription="@string/mon_velo" />
La notation @drawable/nom référence l’image portant ce nom dans l’un des dossiers.
NB: les dossiers res/mipmaps-* contiennent la même image à des définitions différentes, pour
correspondre à différents téléphones et tablettes. Ex: mipmap-hdpi contient des icônes en 72x72
pixels.
<resources>
<string-array name="planetes">
<item>Mercure</item>
<item>Venus</item>
<item>Terre</item>
<item>Mars</item>
</string-array>
</resources>
2.2.13. Autres
D’autres notations existent :
• @style/nom pour des définitions de res/style
• @menu/nom pour des définitions de res/menu
Certaines notations, @package:type/nom font référence à des données prédéfinies, comme :
• @android:style/TextAppearance.Large
• @android:color/black
Il y a aussi une notation en ?type/nom pour référencer la valeur de l’attribut nom, ex :
?android:attr/textColorSecondary.
46
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
47
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<LinearLayout ...>
<TextView android:text="@string/bonjour" ... />
</LinearLayout>
qui est référencé par son identifiant R.layout.nom_du_fichier (donc ici c’est R.layout.main) dans
le programme Java :
TextView tv = findViewById(R.id.message);
<LinearLayout ...>
<TextView
android:id="@+id/message"
android:text="@string/bonjour" />
</LinearLayout>
48
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
49
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<Button
android:layout_margin="10dp"
android:layout_marginTop="15dp"
android:padding="10dp"
android:paddingLeft="20dp" />
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:text="Ok"
android:layout_width="wrap_content"
50
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
android:layout_height="wrap_content"/>
<Button android:text="Annuler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
51
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<TableLayout ...>
<TableRow>
<vue 1.1 .../>
<vue 1.2 .../>
</TableRow>
<TableRow>
<vue 2.1 .../>
<vue 2.2 .../>
</TableRow>
<TableLayout>
<TableLayout
android:stretchColumns="1,2"
android:shrinkColumns="0,3"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
52
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<TextView android:id="@+id/titre"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentLeft="true" .../>
<EditText android:layout_below="@id/titre"
android:layout_alignParentRight="true"
android:layout_alignParentLeft="true" .../>
Et ainsi de suite.
2.4.2. TextView
Le plus simple, il affiche un texte statique, comme un titre. Son libellé est dans l’attribut
android:text.
<TextView
android:id="@+id/tvtitre"
android:text="@string/titre"
... />
53
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
2.4.3. Button
<Button
android:id="@+id/btn_ok"
android:text="@string/ok"
... />
2.4.4. Bascules
<CheckBox
android:id="@+id/cbx_abonnement_nl"
android:text="@string/abonnement_newsletter"
... />
Les ToggleButton sont une variante : . On peut définir le texte actif et le texte inactif avec
android:textOn et android:textOff.
2.4.5. EditText
<EditText
android:id="@+id/email_address"
android:inputType="textEmailAddress"
... />
L’attribut android:inputType spécifie le type de texte : adresse, téléphone, etc. Ça définit le clavier
qui est proposé pour la saisie.
Lire la référence Android pour connaître toutes les possibilités.
54
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
55
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 3
Une activité qui n’est pas déclarée dans le manifeste ne peut pas être lancée.
56
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Un <intent-filter> déclare les conditions de démarrage d’une activité. Celui-ci indique l’activité
principale, celle qu’il faut lancer quand on clique sur son icône.
L’instruction startActivity démarre Activ2. Celle-ci se met au premier plan, tandis que Activ1
se met en sommeil.
Activ1 reviendra au premier plan quand Activ2 se finira ou quand l’utilisateur appuiera sur back.
Ce bout de code est employé par exemple lorsqu’un bouton, un menu, etc. est cliqué. Seule contrainte :
que ces deux activités soient déclarées dans AndroidManifest.xml.
57
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
String url =
"https://perso.univ-rennes1.fr/pierre.nerzic/Android";
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
L’action VIEW avec un URI (généralisation d’un URL) est interprétée par Android, cela fait ouvrir
automatiquement le navigateur.
Cela consiste à créer un Intent d’action MAIN et de catégorie LAUNCHER pour la classe MainActivity
de l’autre application.
58
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Définir l’attribut android:sharedUserId avec une chaîne identique à une autre application, et signer
les deux applications avec le même certificat, permet à l’une d’accéder à l’autre.
3.2. Applications
3.2.1. Fonctionnement d’une application
Au début, le système Android lance l’activité qui est marquée action=MAIN et catégorie=LAUNCHER
dans AndroidManifest.xml.
Ensuite, d’autres activités peuvent être démarrées. Chacune se met « devant » les autres comme sur
une pile. Deux cas sont possibles :
• La précédente activité se termine, on ne revient pas dedans.
Par exemple, une activité où on tape son login et son mot de passe lance l’activité principale et
se termine.
• La précédente activité attend la fin de la nouvelle car elle lui demande un résultat en retour.
Exemple : une activité de type liste d’items lance une activité pour éditer un item quand on
clique longuement dessus, mais attend la fin de l’édition pour rafraîchir la liste.
finish() fait terminer l’activité courante. L’utilisateur ne pourra pas faire back dessus, car elle
disparaît de la pile.
59
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
60
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Ce code identifie l’activité lancée, afin de savoir plus tard que c’est d’elle qu’on revient. Par exemple,
on pourrait lancer au choix plusieurs activités : édition, copie, suppression d’informations. Il faut
pouvoir les distinguer au retour.
Consulter cette page.
Ensuite, il faut définir une méthode callback qui est appelée lorsqu’on revient dans notre activité :
@Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data)
{
// uti a fait back
if (resultCode == Activity.RESULT_CANCELED) return;
// selon le code d'appel
switch (requestCode) {
case APPEL_ACTIV2: // on revient de Activ2
...
}
}
setResult(RESULT_OK);
finish();
setResult(RESULT_CANCELED);
finish();
Dans ces deux cas, on revient dans l’activité appelante (sauf si elle-même avait fait finish().
61
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Intent intent =
new Intent(this, DeleteInfoActivity.class);
intent.putExtra("idInfo", idInfo);
intent.putExtra("hiddencopy", hiddencopy);
startActivity(intent);
putExtra(nom, valeur) rajoute un couple (nom, valeur) dans l’intent. La valeur doit être sérialis-
able : nombres, chaînes et structures simples.
62
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Par défaut, c’est un objet neutre ne contenant que des informations Android.
Il est possible de le sous-classer afin de stocker des variables globales de l’application.
// initialisation du contexte
@Override public void onCreate() {
super.onCreate();
varglob = 3;
}
}
63
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
3.3. Activités
3.3.1. Présentation
Voyons maintenant comment fonctionnent les activités.
• Démarrage (à cause d’un Intent)
• Apparition/masquage sur écran
• Terminaison
Une activité se trouve dans l’un de ces états :
• active (resumed) : elle est sur le devant, l’utilisateur peut jouer avec,
• en pause (paused) : partiellement cachée et inactive, car une autre activité est venue devant,
• stoppée (stopped) : totalement invisible et inactive, ses variables sont préservées mais elle ne
tourne plus.
64
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override signifie que cette méthode remplace celle héritée de la superclasse. Il faut quand même
l’appeler sur super en premier.
@Override
public void onDestroy() {
// obligatoire
super.onDestroy();
// fermer la base
db.close();
}
65
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
public void onSaveInstanceState(Bundle etat) {
// enregistrer l'état courant
etat.putInt(ETAT_SCORE, mScoreJoueur);
super.onSaveInstanceState(etat);
}
@Override
protected void onRestoreInstanceState(Bundle etat) {
super.onRestoreInstanceState(etat);
// restaurer l'état précédent
mScoreJoueur = etat.getInt(ETAT_SCORE);
}
Ces deux méthodes sont appelées automatiquement (sorte d’écouteurs), sauf si l’utilisateur tue
l’application. Cela permet de reprendre l’activité là où elle en était.
Voir IcePick pour une automatisation de ce concept.
66
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Cette méthode cherche la vue qui possède cet identifiant dans le layout de l’activité. Si cette vue
n’existe pas (mauvais identifiant, ou pas créée), la fonction retourne null.
Un mauvais identifiant peut être la raison d’un bug. Cela peut arriver quand on se trompe de layout
pour la vue. C’est néanmoins surveillé par Android Studio.
Pour éviter les problèmes de typage et de vues absentes d’un layout, il existe un dispositif appelé
ViewBindings. Ce sont des classes qui sont générées automatiquement à partir de chaque layout et
dont les variables membres sont les différentes vues.
Par exemple, soit un layout appelé activity_main.xml :
<LinearLayout ...>
</LinearLayout>
Cela fait générer une classe appelée ActivityMainBinding.java et contenant à peu près ceci :
Chaque vue du layout xml possédant un identifiant est reliée à une variable membre publique dans
cette classe, et la vue racine est accessible par getRoot().
Une méthode statique inflate instancie les différentes vues et la vue racine peut être fournie à
setContentView.
67
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ui = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(ui.getRoot());
// exemple d'emploi
ui.titre.setText("super cool !");
}
plugins {
id 'com.android.application'
}
android {
compileSdkVersion ...
buildToolsVersion "..."
defaultConfig {
...
}
buildFeatures {
viewBinding = true // génération des ViewBindings
}
<TextView android:id="@+id/titre"
android:lines="2"
android:text="@string/debut" />
En Java :
Consulter leur documentation pour les propriétés, qui sont extrêmement nombreuses.
68
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<Button
android:onClick="onValider"
android:id="@+id/btnValider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/valider"/>
69
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Dans la méthode onClick, il faut employer la syntaxe MonActivity.this pour manipuler les variables
et méthodes de l’activité sous-jacente.
Il est intéressant de transformer cet écouteur en expression lambda. C’est une écriture plus compacte
qu’on retrouve également en JavaScript, et très largement employée en Kotlin.
Button btn = ui.btnValider;
btn.setOnClickListener((View btn) -> {
// faire quelque chose
});
70
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
71
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
72
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 4
Application liste
Durant les prochaines semaines, nous allons nous intéresser aux applications de gestion d’une liste
d’items.
• Stockage d’une liste
• Affichage d’une liste, adaptateurs
• Consultation et édition d’un item
figure 24
4.1. Présentation
4.1.1. Principe général
On veut programmer une application pour afficher et éditer une liste d’items.
73
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
• Cette semaine, la liste est stockée dans un tableau type ArrayList ; en semaine 6, ça sera dans
une BDD Realm.
• L’écran est occupé par un RecyclerView. C’est une vue spécialisée dans l’affichage de listes
quelconques.
Consulter ces explications qui sont très claires et très complètes, mais qui n’utilisent pas les
ViewBindings.
Il y a aussi la documentation Google, assez compliquée, sur les RecyclerView, et celle là sur les
adaptateurs.
Anciennement, on utilisait des ListView, mais ils sont délaissés car trop peu polyvalents.
Lui rajouter tous les accesseurs (getters) et modificateurs (setters) pour en faire un JavaBean :
objet Java simple (POJO) composé de variables membres privées initialisées par le constructeur, et
d’accesseurs.
74
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
final signifie constant, initdata ne pourra pas être réaffecté (par contre, ses cases peuvent être
réaffectées).
void onCreate(...)
{
...
NB: Arrays.asList crée une liste non modifiable, c’est pour ça qu’on la recopie dans un ArrayList.
La variable est du type List (superclasse abstraite) et affectée avec un ArrayList. La raison est
qu’il faut de préférence toujours employer le type le plus général qui possède les méthodes voulues.
Mais quand c’est une classe abstraite (une interface), on l’instancie avec une sous-classe non-abstraite.
Par exemple un List peut être instancié avec un ArrayList ou un LinkedList. On choisit en
fonction des performances voulues : un ArrayList est très rapide en accès direct, mais très lent en
insertion. C’est l’inverse pour un LinkedList.
Quelques méthodes utiles de la classe abstraite List, héritées par ArrayList :
75
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<resources>
<string-array name="noms">
<item>Mercure</item>
<item>Venus</item>
...
</string-array>
<integer-array name="distances">
<item>58</item>
<item>108</item>
...
</integer-array>
</resources>
Intérêt : traduire les noms des planètes dans d’autres langues en créant des variantes, ex:
res/values-en/arrays.xml
Ensuite, on récupère ces ressources tableaux pour remplir le ArrayList :
Ça semble plus complexe, mais c’est préférable à la solution du tableau pré-initialisé pour la séparation
entre programme et données.
4.1.8. Remarques
Cette semaine, les données sont représentées dans un ArrayList volatile : quand on ferme l’activité,
les données sont perdues. Pour faire un peu mieux que cela, il faut définir une classe Application
76
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
comme en semaine 3 et mettre ce tableau ainsi que son initialisation dedans. Ainsi, le tableau devient
disponible dans toutes les activités de l’application. Voir le TP4.
Cependant, les données ne sont encore pas permanentes. Elles sont perdues quand on quitte
l’application.
En semaine 6, nous verrons comment utiliser une base de données Realm locale ou distante, au lieu de
ce tableau dynamique, ce qui résout le problème de manière élégante et rend les données persistantes
d’une exécution à l’autre.
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Notez la présence du package de cette vue dans la balise. Elle fait partie de l’ensemble androidx qui
sera expliqué au cours n°5.
77
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
4.3.2. Concepts
La vue ne sert qu’à afficher les éléments de la liste. En réalité, seuls quelques éléments seront visibles
en même temps. Cela dépend de la hauteur de la liste et la hauteur des éléments.
Le principe du RecyclerView est de ne gérer que les éléments visibles. Ceux qui ne sont pas visibles
ne sont pas mémorisés. Mais lorsqu’on fait défiler la liste ainsi qu’au début, de nouveaux éléments
doivent être rendus visibles.
Le RecyclerView demande alors à l’adaptateur de lui instancier (inflate) les vues pour afficher les
éléments.
Le nom « RecyclerView » vient de l’astuce : les vues qui deviennent invisibles à cause du défilement
vertical sont recyclées et renvoyées de l’autre côté mais en changeant seulement le contenu à afficher.
78
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
4.3.4. ViewHolders
Pour permettre ce recyclage, il faut que les vues associées à chaque élément puissent être soit recréées,
soit réaffectées. On les appelle des ViewHolders, parce que ce sont des mini-containers qui regroupent
des vues de base (nom de la planète, etc.)
Un ViewHolder est une classe associée à une donnée de base, ex: une planète et qui permet son
affichage dans un RecyclerView.
Le ViewHolder est très similaire à un ViewBinding et possède des méthodes supplémentaires pour
placer les informations dans les différentes vues.
79
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
ui.distance.setText(
Integer.toString(planete.getDistance()));
}
}
La classe PlaneteViewHolder mémorise un PlaneteBinding, c’est à dire l’ensemble des vues représen-
tant une planète à l’écran. Ce ViewBinding sera fourni par l’adaptateur1 .
La méthode setPlanete met à jour ces vues à partir de la donnée passée en paramètre. Cette
méthode est appelée par l’adaptateur lors du recyclage.
D’autres méthodes seront ajoutées pour gérer les clics sur les éléments.
Cette classe va gérer l’affichage des éléments individuels et aussi gérer la liste dans son ensemble.
Pour cela, on définit un constructeur et on doit surcharger trois méthodes.
1
La création du ViewBinding est faite en amont, par l’adaptateur. On ne peut pas faire autrement car le constructeur
de la superclasse ViewHolder demande une interface déjà créée.
80
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
PlaneteAdapter(List<Planete> liste) {
this.liste = liste;
}
...
La liste est stockée dans l’adaptateur. C’est un partage de référence : une seule allocation en mémoire.
Elle est appelée au début de l’affichage de la liste, pour initialiser ce qu’on voit à l’écran.
inflate = transformer un fichier XML en vues Java.
Enfin, définir la méthode qui recycle les ViewHolder :
@Override public void
onBindViewHolder(PlaneteViewHolder holder, int position)
{
Planete planete = liste.get(position);
holder.setPlanete(planete);
}
Cette méthode est appelée pour remplir un ViewHolder avec l’un des éléments de la liste, celui qui
est désigné par position (numéro dans la liste à l’écran). C’est très facile avec le setter.
D’autres méthodes seront ajoutées pour gérer les clics sur les éléments.
81
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
// dimensions constantes
ui.recycler.setHasFixedSize(true);
4.4.2. LayoutManager
Ensuite, et c’est indispensable, le RecyclerView doit savoir comment organiser les éléments : en liste,
en tableau, en grille. . .
Cela se fait avec un LayoutManager :
...
// layout manager
RecyclerView.LayoutManager lm =
new LinearLayoutManager(this);
ui.recycler.setLayoutManager(lm);
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager" />
82
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Le 3e paramètre est un booléen qui indique dans quel sens se fait le défilement, vers la droite ou vers
la gauche.
GridLayoutManager lm =
new GridLayoutManager(this,
2, // lignes ou colonnes
RecyclerView.HORIZONTAL, // direction
false); // sens
83
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
// séparateur
DividerItemDecoration dividerItemDecoration =
new DividerItemDecoration(
this, DividerItemDecoration.VERTICAL);
ui.recycler.addItemDecoration(dividerItemDecoration);
84
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
85
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Remarquez comment on fait référence à l’interface définie dans la classe PlaneteAdapter. C’est
comme View.OnClickListener.
Les ViewHolders doivent aussi recevoir les événements puis déclencher l’écouteur :
86
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
87
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 5
Ergonomie
88
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
L’attribut showAsAction vaut "always", "ifRoom" ou "never" selon la visibilité qu’on souhaite
dans la barre d’action. Cet attribut est à modifier en app:showAsAction si on utilise androidx.
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// ajouter mes items de menu
getMenuInflater().inflate(R.menu.nom_du_menu, menu);
// ajouter les items du système s'il y en a
return super.onCreateOptionsMenu(menu);
}
89
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
...
// ajouter des items manuellement
menu.add(Menu.NONE, MENU_ITEM1, ordre, titre).setIcon(image);
...
}
Cette fois, vous devrez choisir un identifiant pour les items. L’ordre indique la priorité de cet item ;
mettre Menu.NONE s’il n’y en a pas.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_creer:
...
return true;
case MENU_ITEM1:
...
return true;
...
default: return super.onOptionsItemSelected(item);
}
}
90
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
android:showAsAction="always"
android:title="@string/menu_more">
<menu>
<item android:id="@+id/menu_item3" ... />
<item android:id="@+id/menu_item4" ... />
</menu>
</item>
</menu>
91
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Il y a une astuce, mais c’est un remède un peu faible : utiliser la propriété order des items de menu
pour stocker la position de l’élément cliqué dans la liste. Cette propriété permet normalement de les
classer pour les afficher dans un certain ordre, mais comme on ne s’en sert pas. . .
On est obligé de faire ainsi car il manque une propriété custom dans la classe MenuItem. Une meilleure
solution serait de sous-classer cette classe avec la propriété qui nous manque, mais il faudrait modifier
beaucoup plus de choses.
Une dernière remarque : il n’est pas possible d’afficher un icône à côté du titre d’item. C’est un choix
délibéré dans Android.
@Override
public boolean onContextItemSelected(MenuItem item) {
int position = item.getOrder(); // récup position
Planete planete = liste.get(position); // récup item
switch (item.getItemId()) {
case PlaneteViewHolder.MENU_EDIT:
92
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Toast.makeText(getContext(),
R.string.item_supprime, Toast.LENGTH_SHORT).show();
5.3.2. Dialogues
Un dialogue est une petite fenêtre qui apparaît au dessus d’un écran pour afficher ou demander
quelque chose d’urgent à l’utilisateur, par exemple une confirmation.
figure 33
93
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
// affichage du dialogue
builder.show();
94
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Ensuite cela ressemble à ce qu’on fait dans onCreate d’une activité : installation du layout et des
écouteurs pour les boutons.
95
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
96
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
ui = PlaneteInfosBinding.inflate(inflater, container, false);
// écouteurs, adaptateur...
return ui.getRoot();
}
}
97
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
public View onCreateView(LayoutInflater inflater, ...) {
...
... récupérer la liste (application ou BDD)
adapter = new PlaneteAdapter(liste);
... layout manager, séparateur, écouteurs...
return ui.getRoot();
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater mInf)
{
mInf.inflate(R.menu.edit_fragment, menu);
super.onCreateOptionsMenu(menu, mInf);
}
98
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
... />
</LinearLayout>
Chaque fragment doit avoir un identifiant et un nom de classe complet avec tout le package. Ne pas
oublier les attributs des tailles et éventuellement poids.
<LinearLayout xmlns:android="..."
android:orientation="horizontal" ... >
<fragment android:id="@+id/frag_liste" ... />
</LinearLayout>
• res/layout-land/activity_main.xml en paysage :
<LinearLayout xmlns:android="..."
android:orientation="horizontal" ... >
<fragment android:id="@+id/frag_liste" ... />
<fragment android:id="@+id/frag_infos" ... />
</LinearLayout>
99
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Notez que le fragment sera aussi mis en @Nullable dans le view binding de l’activité.
Ce sera l’activité principale qui sera cet écouteur dans l’adaptateur du fragment, grâce à :
@Override
public View onCreateView(LayoutInflater inflater, ...) {
...
adapter.setOnItemClickListener(
(PlaneteAdapter.OnItemClickListener) getActivity());
...
100
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
101
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
public Classe2() {
...
objet1.setOnEvenementListener(this);
}
5.5.2. Présentation
Il y a deux concepts mis en jeu :
• Une activité pour afficher et modifier les préférences.
• Une sorte de base de données qui stocke les préférences,
– booléens,
– nombres : entiers, réels. . . ,
– chaînes et ensembles de chaînes.
Chaque préférence possède un identifiant. C’est une chaîne comme "prefs_nbmax". La base de
données stocke une liste de couples (identifiant, valeur).
Voir la documentation Android. Les choses changent beaucoup d’une version à l’autre d’API.
102
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
5.5.4. Explications
Ce fichier xml définit à la fois :
• Les préférences :
– l’identifiant : android:key
– le titre résumé : android:title
– le sous-titre détaillé : android:summary
– la valeur initiale : android:defaultValue
• La mise en page. C’est une sorte de layout contenant des cases à cocher, des zones de saisie. . .
Il est possible de créer des pages de préférences en cascade comme par exemple, les préférences
système.
Consulter la doc pour connaître tous les types de préférences.
NB: le résumé n’affiche malheureusement pas la valeur courante. Consulter stackoverflow pour une
proposition.
Les getters ont deux paramètres : l’identifiant de la préférence et la valeur par défaut.
103
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Pour les entiers, il y a bug important (février 2015). La méthode getInt plante. Voir stackoverflow
pour une solution. Sinon, il faut passer par une conversion de chaîne en entier :
104
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<uses-sdk android:minSdkVersion="17"
android:targetSdkVersion="28" />
Avec ce manifeste, si la tablette n’est pas au moins en API niveau 17, l’application ne sera pas installée.
L’application est garantie pour bien fonctionner jusqu’à l’API 28 incluse.
105
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
implementation "androidx.appcompat:appcompat:1.0.2"
implementation "androidx.recyclerview:recyclerview:1.1.0"
<androidx.recyclerview.widget.RecyclerView .../>
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
106
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
android {
compileSdkVersion 29
defaultConfig {
applicationId "mon.package"
minSdkVersion 16
targetSdkVersion 29
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
...
implementation "androidx.appcompat:appcompat:1.0.2"
implementation "androidx.recyclerview:recyclerview:1.1.0"
...
}
On rajoute les éléments nécessaires. Il faut aller voir la documentation de chaque chose employée
pour savoir quelle dépendance rajouter, et vérifier son numéro de version pour avoir la dernière.
5.6.8. Programmation
Enfin, il suffit de faire appel à ces classes pour travailler. Elles sont par exemple dans le package
androidx.fragment.app.
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;
Il y a quelques particularités, comme une classe AppCompatButton qui est employée automatiquement
à la place de Button dans les activités du type AppCompatActivity. Le mieux est d’étudier les
documentations pour arriver à utiliser correctement tout cet ensemble.
107
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 6
Realm
Le cours de cette semaine va vous apprendre à stocker des informations dans un SGBD appelé
Realm. Ce système utilise de simples objets Java pour représenter les n-uplets et offre de nombreuses
possibilités d’interrogation.
• Principes
• Modèles de données
• Requêtes
• Adaptateurs
Avant tout, on va commencer par un plugin pour AndroidStudio bien pratique, Lombok.
6.1.2. Exemple
Voici comment générer automatiquement les setters et getters et la méthode toString() :
import lombok.*;
108
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
L’avantage principal est que la classe reste très facilement lisible, et il y a pourtant toutes les méthodes.
La méthode générée toString() affiche this proprement : Personne(id=3, nom="Nerzic",
prenom="Pierre").
@ToString
@Getter
public class Personne
{
private int id;
@Setter private String nom;
@Setter private String prenom;
}
On aura un getter pour chaque variable et un setter seulement pour le nom et le prénom.
annotationProcessor 'org.projectlombok:lombok:1.18.16'
implementation 'org.projectlombok:lombok:1.18.16'
109
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
6.2. Realm
6.2.1. Définition de Realm
Realm est un mécanisme permettant de transformer de simples classes Java en sortes de tables de
base de données. La base de données est transparente, cachée dans le logiciel. Le SGBD tient à jour
la liste des instances et permet de faire l’équivalent des requêtes SQL.
C’est ce qu’on appelle un ORM (object-relational mapping).
Chaque instance de cette classe est un n-uplet dans la table. Les variables membres sont les attributs
de la table.
Realm est très bien expliqué sur ces pages. Ce cours suit une partie de cette documentation.
classpath 'io.realm:realm-gradle-plugin:7.0.8'
110
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
import io.realm.Realm;
public class MyApplication extends Application
{
@Override
public void onCreate()
{
super.onCreate();
Realm.init(this);
}
}
@Override
public void onCreate()
{
super.onCreate();
...
realm = Realm.getDefaultInstance();
...
}
}
@Override
public void onDestroy()
{
super.onDestroy();
realm.close();
}
}
On peut définir une classe RealmActivity qui hérite de Activity et qui effectue ces deux opérations.
On n’a donc plus qu’à hériter de RealmActivity sans se soucier de rien.
111
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
RealmConfiguration conf =
new RealmConfiguration.Builder().inMemory().build();
realm = Realm.getInstance(conf);
Cela a automatiquement créé une table de Produit à l’intérieur de Realm. Par contre, cette table
n’est pas visible en tant que telle, voir RealmStudio pour un outil d’édition de la base.
Elle reste donc extrêmement lisible. On peut se concentrer sur les algorithmes.
NB: dans la suite, l’emploi du plugin Lombok sera implicite.
112
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
import io.realm.annotations.Required;
Si vous tentez d’affecter designation avec null, ça déclenchera une exception. On ne peut hélas pas
placer cette annotation sur un autre type d’objet, voir la doc de @Required.
import io.realm.annotations.PrimaryKey;
Si vous tentez d’affecter à id la même valeur pour deux produits différents, vous aurez une exception.
113
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
La variable produit pourra soit contenir null, soit désigner un objet. Malheureusement, on ne peut
pas (encore) appliquer @Required sur une référence d’objet.
114
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
realm.beginTransaction();
Produit produit = realm.createObject(Produit.class, 1L);
produit.setDesignation("brosse à dent");
produit.setPrixUnitaire(4.95);
realm.commitTransaction();
realm.beginTransaction();
Produit produitRealm = realm.copyToRealm(produitJava);
realm.commitTransaction();
Le problème est que ça crée deux objets, or seul le dernier est connu de Realm.
realm.beginTransaction();
produit.setDesignation("fil dentaire");
produit.setPrixUnitaire(0.95);
realm.commitTransaction();
Si vous oubliez de placer les modifications dans une transaction, vous aurez une IllegalStateException.
D’autre part, contrairement à SQL, vous ne pourrez pas modifier la clé primaire d’un objet après sa
création.
realm.beginTransaction();
produit.deleteFromRealm();
realm.commitTransaction();
115
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
6.5.2. Sélections
La requête la plus simple consiste à récupérer la liste de tous les n-uplets d’une table :
Cette manière d’écrire des séquences d’appels à des méthodes Java s’appelle désignation chaînée,
fluent interface en anglais.
La classe du RealmResults doit être la même que celle fournie à where.
En fait, la méthode where est très mal nommée. Elle aurait due être appelée from.
La méthode equalTo(nom_champ, valeur) définit une condition sur le champ fourni par son nom et
la valeur. D’autres comparaisons existent, voir cette page :
• pour tous les champs : equalTo, notEqualTo, in
• pour les nombres : between, greaterThan, lessThan, greaterThanOrEqualTo, lessThanOrEqualTo
• pour les chaînes : contains, beginsWith, endsWith, like
116
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Pour chaque CLASSE de type RealmObject, ça génère automatiquement une classe appelée
CLASSEFields contenant des chaînes constantes statiques du nom des champs de la classe :
public final class ProduitFields {
public static final String ID = "id";
public static final String DESIGNATION = "designation";
public static final String MARQUE = "marque";
...
};
L’emploi du RealmFieldNamesHelper alourdit un peu l’écriture, mais on est certain que le programme
fonctionnera, ou alors ne se compilera pas si on change le schéma de la base.
117
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
6.5.8. Négations
La méthode not() permet d’appliquer une négation sur la condition qui la suit :
Le second paramètre de sort est optionnel. S’il est absent, c’est un tri croissant sur le champ indiqué
en premier paramètre.
118
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Cette requête va chercher parmi tous les produits liés et retourne les achats qui sont liés à au moins
un produit dentifrice :
Mais impossible de chercher les achats qui ne concernent que des dentifrices ou tous les dentifrices.
119
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
120
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
realm.beginTransaction();
achats_dentif.deleteAllFromRealm();
dentifrices.deleteAllFromRealm();
realm.commitTransaction();
Le booléen true à fournir à la superclasse indique que les données se mettent à jour automatiquement.
Ensuite, il faut programmer la méthode de création des vues :
En fait, c’est la même méthode que dans le cours n°4 avec les PlaneteView.
Ensuite, il faut programmer la méthode qui place les informations dans les vues :
121
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Elle est utilisée pour obtenir l’identifiant de l’objet cliqué dans la liste, voir onItemClick plus loin.
Comme la liste est vivante, toutes les modifications seront automatiquement reportées à l’écran sans
qu’on ait davantage de choses à programmer.
Le paramètre position donne la position dans la liste, et le paramètre id n’est que l’identifiant de la
vue cliquée dans la liste, pas l’identifiant de l’élément cliqué, c’est pour cela qu’on utilise getItemId
pour l’obtenir.
122
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 7
Dessin 2D interactif
Le cours de cette semaine concerne le dessin de figures 2D et les interactions avec l’utilisateur.
• CustomView et Canevas
• Un exemple de boîte de dialogue utile
7.1. Dessin en 2D
7.1.1. But
figure 39
7.1.2. Principes
Une application de dessin 2D doit définir une sous-classe de View et surcharger la méthode onDraw.
Voici un exemple :
package fr.iutlan.dessin;
public class DessinView extends View {
Paint mPeinture;
123
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Bitmap bm =
Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
C’est déjà fait pour le canvas fourni à la méthode onDraw. On obtient le bitmap de la vue avec
getDrawingCache().
124
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Il est préférable de créer les peintures dans le constructeur de la vue ou une autre méthode, mais
surtout pas dans la méthode onDraw.
7.1.8. Motifs
Il est possible de créer une peinture basée sur un motif. On part d’une image motif.png dans le
dossier res/drawable qu’on emploie comme ceci :
Bitmap bmMotif = BitmapFactory.decodeResource(
context.getResources(), R.drawable.motif);
BitmapShader shaderMotif = new BitmapShader(bmMotif,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
mPaintMotif = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintMotif.setShader(shaderMotif);
mPaintMotif.setStyle(Paint.Style.FILL_AND_STROKE);
Cette peinture fait appel à un Shader. C’est une classe permettant d’appliquer des effets progressifs,
tels qu’un dégradé ou un motif comme ici (BitmapShader).
125
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
7.1.9. Shaders
Voici la réalisation d’un dégradé horizontal basé sur 3 couleurs :
figure 40
Le dégradé précédent est base sur trois couleurs situées aux extrémités et au centre du rectangle.
On fournit donc deux tableaux, l’un pour les couleurs et l’autre pour les positions des couleurs
relativement au dégradé, de 0.0 à 1.0.
Le dégradé possède une dimension, 100 pixels de large. Si la figure à dessiner est plus large, la couleur
sera maintenue constante avec l’option CLAMP. D’autres options permettent de faire un effet miroir,
MIRROR, ou redémarrer au début REPEAT.
Cette page présente les shaders et filtres d’une manière extrêmement intéressante. Comme vous
verrez, il y a un grand nombre de possibilités.
7.1.11. « Dessinables »
Les canvas servent à dessiner des figures géométriques, rectangles, lignes, etc, mais aussi des Drawable,
c’est à dire des « choses dessinables » telles que des images bitmap ou des formes quelconques. Il
existe beaucoup de sous-classes de Drawable.
126
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Bitmap bm = BitmapFactory
.decodeResource(getResources(), R.drawable.image);
Drawable d = new BitmapDrawable(getResources(),bm);
figure 41
7.1.13. Variantes
Android permet de créer des « dessinables » à variantes par exemple pour des boutons personnalisés.
<item android:drawable="@drawable/button_pressed"
android:state_pressed="true" />
<item android:drawable="@drawable/button_checked"
android:state_checked="true" />
<item android:drawable="@drawable/button_default" />
</selector>
L’une ou l’autre des images sera choisie en fonction de l’état du bouton, enfoncé, relâché, inactif.
127
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
128
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
...
break;
}
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
figure = Figure.creer(typefigure, color);
figure.setReference(x, y);
figures.add(figure);
break;
case MotionEvent.ACTION_MOVE:
if (figures.size() < 1) return true;
figure = figures.getLast();
figure.setCoin(x,y);
break;
}
invalidate();
129
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Ensuite, à chaque événement, on se décide en fonction de cet état, voir transparent suivant.
130
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
7.3.3. Concepts
Plusieurs concepts interviennent dans ce sélecteur de couleur :
• La fenêtre dérive de DialogFragment, elle affiche un dialogue de type AlertDialog avec des
boutons Ok et Annuler,
• Cet AlertDialog contient une vue personnalisée contenant des SeekBar pour régler les com-
posantes de couleur,
• Les SeekBar du layout ont des callbacks qui mettent à jour la couleur choisie en temps réel,
• Le bouton Valider du AlertDialog déclenche un écouteur dans l’activité qui a appelé le sélecteur.
131
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
132
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
7.3.8. Écouteurs
Tous ces SeekBar ont un écouteur similaire :
SeekBar sbRouge = findViewById(R.id.sbRouge);
sbRouge.setOnSeekBarChangeListener(
new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar,
int progress, boolean fromUser) {
mColor = Color.argb(
Color.alpha(mColor), progress,
Color.green(mColor), Color.blue(mColor));
}
});
Celui-ci change seulement la composante rouge de la variable mColor. Il y a les mêmes choses pour
les autres composantes.
133
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
134
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 8
Test logiciel
Le cours de cette semaine est consacré au test systématique d’une application Android. Cela se fait
en programmant de nouvelles classes et méthodes spéciales, qui font des vérifications sur les classes et
fonctions de l’application.
Tester, c’est vérifier qu’une classe et ses méthodes font réellement ce qui est prévu dans les spécifications.
Tester un logiciel de manière automatisée permet de garantir une non-régression lors du développement.
Sans tests systématiques, il est facile de casser involontairement un logiciel complexe, en particulier
en développement agile.
AndroidStudio permet d’effectuer deux sortes de tests :
• des tests unitaires pour vérifier des classes individuellement,
• des tests sur AVD pour vérifier le comportement de l’interface.
8.1. Introduction
8.1.1. Principe de base
Le principe général consiste à programmer des fonctions qui vont appeler d’autres fonctions pour
vérifier leurs résultats.
Soit une fonction float racine(float x) qui est censée calculer la racine carrée d’un réel positif.
Pour savoir si elle fonctionne bien, il faudrait calculer racine(x) ∗ racine(x) pour chaque nombre réel
x positif, c’est à dire appliquer sa définition mathématique ∀x ∈ R+ , racine(x)2 = x.
Ainsi, la fonction de test pourrait s’écrire :
8.1.2. Limitations
On voit qu’il n’est pas possible de vérifier chaque réel. On se limite à quelques valeurs représentatives
et on suppose que la fonction est correcte pour les autres.
D’autre part, il est possible que le test emploie la même définition que la fonction, ce qui ne prouvera
pas qu’elle est bonne ; par exemple, la même série limitée pour sinus. Pour tester correctement, il
faut trouver un algorithme totalement différent.
135
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Souvent, le testeur est indépendant des programmeurs. Il ne doit pas savoir comment les fonctions
ont été codées. Il doit s’appuyer sur le cahier des charges et explorer toutes les limites. Une grande
expérience en programmation est utile pour faire de bons tests.
On doit toujours comparer deux réels v1 et v2 par |v1 − v2 | ≤ , jamais avec v1 == v2 . Le est à
déterminer empiriquement.
136
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
8.2.3. JUnit4
Soit une classe Chose à tester. On doit programmer une classe contenant des méthodes annotées par
@Test :
package fr.iutlan.tp8;
import org.junit.Test;
import static org.junit.Assert.assertEquals; // <-- static
137
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
8.2.4. Explications
La bibliothèque JUnit4 définit l’annotation @Test pour dire que la méthode annotée est un test. Dans
ce test, assertEquals vérifie l’égalité entre une valeur et un résultat d’appel de fonction.
L’API contient de nombreuses directives comme assertEquals, appelées assertions, permettant
toutes sortes de vérifications. Le principe reste toujours de lancer l’exécution d’une méthode et
d’analyser le résultat :
• comparer le résultat à une constante : égalité, différence,
• vérifier l’absence ou la présence d’une exception,
• vérifier que le temps d’exécution ne dépasse pas une limite.
Quand une assertion échoue, cela déclenche une AssertionError.
Malheureusement, JUnit affiche la trace de la pile, ce qui n’est pas très agréable.
import java.lang.Math;
138
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Test
public void testCarre()
{
assertEquals("carre(5)=25", 25, Chose.carre( 5));
assertEquals("carre(-2)=4", 4, Chose.carre(-2));
}
@Test(expected=ArithmeticException.class)
public void testRacineNegative()
{
float rac = racine(-2); // doit déclencher une exception
}
@Test(expected=NumberFormatException.class)
public void testParseNonInt()
{
int n = Integer.parseInt("1001 nuits");
}
139
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Test(timeout=100)
public void testDuree()
{
Chose.calcul();
}
Ces deuxièmes paramètres de assertThat sont appelés matchers (correspondants). Ils sont très
nombreux et très utiles.
assertThat(14, equalTo(14));
assertThat(14, greaterThan(racine(14)));
assertThat(Math.PI, closeTo(3.14, 0.01));
140
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
– isEmptyString()
– hasLength(entier)
– equalTo(texte)
– equalToIgnoringCase(texte)
– equalToIgnoringWhiteSpace(texte)
– containsString(texte)
– endsWith(texte), startsWith(texte)
– matchesPattern(regex) expression régulière Java
• Classes
– instanceOf(classe) passe si calcul est de cette classe
• Agrégation
– allOf(matchers...) passe si tous les matchers passent
– anyOf(matchers...) passe si l’un des matchers passe
assertThat(result, instanceOf(String.class));
assertThat("[email protected]",
allOf(endsWith(".fr"), containsString("@")));
assertThat("[email protected]",
anyOf(endsWith(".org"), endsWith(".fr")));
Attention, certaines agrégations sont impossibles quand les types des matchers ne correspondent pas.
141
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:hamcrest-library:2.2'
@Test
public void testStringUtilsReverse() {
String input = "abc";
assertEquals("cba", result);
}
142
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
assertEquals(3, result);
}
NB: cet exemple n’est pas viable tel quel, Realm ne fonctionne pas sans Android.
Il y a cette convention de nommage des méthodes : setup pour l’initialisation et tearDown pour la
terminaison.
143
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@RunWith(JUnitParamsRunner.class)
public class TestsCalendrier {
@Test @Parameters
public void testBissextile(int annee, boolean bissextile)
{
assertThat(bissextile, Calendrier.isBissextile(annee));
}
Notez l’annotation initiale qui déclenche un exécuteur différent. Notez que la méthode
testBissextile demande des paramètres.
144
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
testImplementation 'junit:junit:4.13.2'
testImplementation 'pl.pragmatists:JUnitParams:1.1.1'
145
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Cela va instancier la variable avec une classe bidon qui se comporte selon l’interface. On pourra
manipuler cette calculatrice comme la vraie. Le problème, c’est que la classe bidon ne peut pas faire
les calculs de la vraie classe. . .
On va donc faire apprendre quelques calculs prédéfinis à cette classe bidon, c’est à dire inventorier
tous les appels à Calculatrice et préparer les réponses qu’elle devrait fournir.
Évidemment, il ne faut pas avoir des dizaines d’appels différents, sinon la classe Calculatrice serait
vraiment indispensable.
146
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Before
public void initCalculatrice() {
...
when(calcu.div(anyFloat(), eq(0)))
.thenThrow(ArithmeticException.class);
}
Il faut associer un matcher comme anyInt(), anyFloat(). . . avec un autre matcher comme
eq(valeur).
when(calcu.div(anyFloat(), 0.0)).thenThrow(...);
when(calcu.sign(floatThat(lessThan(0.0))).thenReturn(-1);
when(calcu.sign(floatThat(nb -> nb > 0))).thenReturn(+1);
when(objet.methode(params)).thenReturn(valeur);
doReturn(valeur).when(objet).methode(params);
On utilise cette seconde syntaxe lorsque l’objet n’est pas initialisé « normalement », par exemple
lorsqu’il est simulé ou espionné, voir plus loin. La première écriture cause une NullPointerException.
147
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
On voudrait tester la méthode toString() de Voiture, mais la classe Personne n’est pas encore
programmée :
Mockito va simuler une personne pour permettre de tester la voiture, mais il faut ajouter l’annotation
@InjectMocks à la voiture :
148
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
calcu.add(x, 1.0f);
verify(calcu).add(anyFloat(), anyFloat());
verify(calcu, times(1)).add(anyFloat(), anyFloat());
verify(calcu, never()).sub(anyFloat(), anyFloat());
TextView tvSortie;
Dans un cas réel, cela peut être nettement plus complexe que ça.
// activité espionnée
@Spy MainActivity activity = new MainActivity();
// fausse vue
@Mock TextView tvSortieMock;
L’idée est :
• d’associer ce tvSortieMock à celui de l’activité, c’est à dire que l’activité va accéder à ce
TextView en croyant manipuler le vrai,
• de simuler les réactions de ce tvSortieMock : simuler sa méthode setText() et voir ce que
l’activité voulait y mettre pour comparer avec ce qu’on attend.
149
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
void findViews() {
tvSortie = findViewById(R.id.sortie);
}
// act
activity.putSortie(3.14159);
// assert
verify(tvSortieMock).setText("3,142"); // format !!
}
L’assertion vérifie que l’activité a demandé au TextView d’afficher cette chaîne. C’est possible car
Mockito a surveillé tous les appels aux méthodes de ce TextView.
150
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Cependant cette classe est générée automatiquement. En plus, elle fait appel à un LayoutInflater
et de nombreuses autres classes.
On ne peut pas y placer nos annotations.
Il y a quand même un moyen de s’en sortir. Il suffit de rajouter un setter dans l’activité, au lieu de
findViews :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ui = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(ui.getRoot());
// activité espionnée
@Spy MainActivity activity = new MainActivity();
// fausses vues
@Mock LinearLayout rootMock; // attention au type !
@Mock TextView tvSortieMock;
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:3.8.0'
androidTestImplementation 'org.mockito:mockito-android:3.8.0'
151
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@RunWith(MockitoJUnitRunner.class)
public class TestsAvecMockito {
onView(matcher)[.perform(action)][.check(assert)]
Par exemple :
onView(withId(R.id.et_prenom)).perform(typeText("Pierre"));
onView(withId(R.id.btn_ok)).check(matches(withText("Ok")));
onView(withId(R.id.btn_ok)).perform(click());
152
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
onView(withId(R.id.et_prenom)).check(withText("Pierre"));
onView(withId(R.id.cb_logged)).check(isChecked());
onView(withId(R.id.sp_metier))
.check(matches(withSpinnerText("enseignant")));
onView(withId(R.id.et_prenom)).perform(typeText("Pierre"));
onView(withText("Ok")).perform(click());
onView(withId(R.id.liste)).perform(
RecyclerViewActions.actionOnItemAtPosition(1, click()));
onData(matcher)[.perform(action)][.check(assert)]
On doit lui fournir un matcher qui désigne les données des adaptateurs de l’écran actuel. Le problème,
c’est que le matcher dépend du type d’adaptateur et c’est compliqué. Alors pour simplifier, on ne
verra que la sélection en fonction de la position :
onData(anything()).atPosition(pos)
153
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
NB: cliquez sur l’icone de téléchargement pour voir les lignes entières. D’autre part, les numéros de
version sont susceptibles de changer.
Cela définit un « scénario » permettant de lancer l’activité automatiquement à chaque test. Voir la
doc pour d’autres types de lancements. Ces scénarios gèrent beaucoup mieux les changements d’états
des activités ainsi que les threads qui en dépendent.
154
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Cette instruction demande l’activité en cours au scénario. onActivity() prend une lambda en
paramètre. Cette lambda n’a qu’un seul paramètre, a qui contient l’activité, et elle est recopiée dans
la variable membre.
8.6.12. Remarques
Vous constatez qu’il est indispensable que l’activité sous test rende publiques un certain nombre
de méthodes, afin qu’on puisse voir si elle se comporte comme prévu. Par exemple, il faut qu’on
puisse injecter une interface utilisateur (View Binding), donc il faut un setter pour cela. Et si on
veut inspecter des variables membres, il faut des getters.
D’autre part il est préférable que les méthodes soient bien découpées, qu’elles ne fassent pas plusieurs
choses à la fois. Cela impose une conception saine du logiciel.
155
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 9
Capteurs
Le cours de cette semaine est consacré aux capteurs des smartphones, et en particulier à ceux
permettant la réalité augmentée.
Il ne faut pas confondre réalité augmentée et réalité virtuelle. Cette dernière consiste à simuler un
environnement 3D ; ce qu’on voit n’existe pas ou est re-créé de toutes pièces.
9.1.2. Applications
• Aide à la conduite de véhicules : avions, voitures, etc.
• Médecine : assistance à la chirurgie
• Tourisme : informations sur les lieux de visites, traduction
• Architecture : visualiser un futur bâtiment, visualiser les risques en cas de tremblement de terre
• Ateliers, usines : aider au montage de mécanismes complexes
• Cinéma : simuler des décors coûteux ou imaginaires
• Visioconférences : rendre visibles les participants à distance
• Loisirs : jeux, arts, astronomie, randonnées
9.1.3. Principes
Dessiner par dessus la vue réelle implique d’avoir :
156
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
157
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
</application>
</manifest>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
Inversement, si vous n’avez besoin que de prendre une photo, vous n’avez pas besoin de tout ce qui
concerne la caméra. Il suffit d’un Intent spécial :
158
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
L’utilisateur est libre d’accepter ou de refuser. S’il refuse, checkSelfPermission ne renverra jamais
plus PERMISSION_GRANTED (sauf si on insiste, cf plus loin).
Ils peuvent être révoqués à tout moment. C’est pour cette raison que les applications doivent tester
leurs droits en permanence.
159
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
@Override
public void onRequestPermissionsResult(...)
{
... regarder les permissions accordées ou refusées ...
}
L’écouteur reçoit le code fourni à requestPermissions, les permissions demandées et les réponses
accordées :
Cet écouteur n’est pas nécessaire si on appelle checkSelfPermission avant chaque action concernée.
La position sur le globe peut être déterminée par triangulation, c’est à dire la mesure des longueurs
des côtés du polyèdre partant du capteur et allant vers trois ou quatre satellites de position connue.
Les distances sont estimées en comparant des horloges extrêmement précises (1µs de décalage = 300m
d’écart).
À défaut d’un GPS (droit manquant ou pas de capteur), on peut obtenir une position grossière (coarse
en anglais) à l’aide des réseaux de téléphonie ou Wifi.
160
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Dans tous les cas, on fait appel à un LocationManager (objet singleton) qui gère les capteurs
de position, appelés « fournisseurs de position » et identifiés par les constantes GPS_PROVIDER et
NETWORK_PROVIDER.
Le principe est de s’abonner à des événements, donc de programmer un écouteur pour ces événements.
Chaque fois que la position change, l’écouteur est appelé avec la nouvelle position.
Les positions sont représentées par la classe Location. C’est essentiellement un triplet (longitude,
latitude, altitude).
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
Location position =
locationManager.getLastKnownLocation(FOURNISSEUR);
161
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
• PERIODICITE donne le temps en millisecondes entre deux événements, mettre 2000 pour toutes
les 2 secondes.
• DISTANCE donne la distance minimale en mètres qu’il faut parcourir pour faire émettre des
événements.
• this ou un écouteur qui implémente les quatre méthodes de LocationListener.
Appeler locationManager.removeUpdates(this); pour cesser de recevoir des événements.
9.3.6. Remarques
Normalement, une activité ne doit demander des positions que lorsqu’elle est active. Quand elle n’est
plus visible, elle doit cesser de demander des positions :
@Override public void onResume() {
super.onResume();
// s'abonner aux événements GPS
if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION))
locationManager.requestLocationUpdates(..., this);
}
162
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
9.4. Caméra
9.4.1. Présentation
La quasi totalité des smartphones possède au moins une caméra capable d’afficher en permanence
un flot d’images prises à l’instant (live display en anglais). Cette caméra est dirigée vers l’arrière de
l’écran. On ne se servira pas de la caméra dirigée vers l’avant.
La direction de la caméra est représentée par une constante, ex: Camera.CameraInfo.CAMERA_FACING_BACK.
Comme pour la position, l’utilisation de la caméra est soumise à autorisations. Elles sont à tester à
chaque étape.
Le principe général est :
• Une sous-classe de View affiche ce que voit la caméra, en mode preview
• La caméra est configurée :
– avec la même résolution que cette vue
– avec la même orientation (portrait/paysage)
NB: ce cours présente une API dépréciée, mais qui fournit des méthodes utiles pour la réalité virtuelle
qui ne sont pas dans la nouvelle. Les API pour caméra changent vraiment très souvent à cause des
fabricants qui proposent chacun des évolutions.
void initSurfaceView()
{
surfaceView = findViewById(R.layout.surface_view);
surfaceView.getHolder().addCallback(this);
}
163
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
On passe par une classe SurfaceHolder dont on appelle la méthode addCallback. L’activité va
réagir aux événements de la SurfaceView (écouteur à 3 méthodes).
Il n’y a pas besoin d’autre chose (reconnaissance faciale, zoom, etc.) pour la réalité augmentée.
164
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Il parcourt toutes les resolutions d’affichage et choisit celle qui est proche de la taille de l’écran.
165
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
camera.setDisplayOrientation(orientation);
camera.getParameters().setRotation(orientation);
...
Remarquez l’emploi de getCameraInfo, notamment son second paramètre. On sent que ce n’est pas
tout à fait du Java derrière (c’est une ancienne API).
Cette méthode sert également pour construire le bon changement de repère en réalité augmentée.
166
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
...
// associer la vue et la caméra
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
camera.stopPreview();
}
Si l’activité revient au premier plan, le système Android appellera onResume. Ces deux fonctions
forment une paire. Il en est de même avec le couple surfaceCreated et surfaceDestroyed.
167
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
// constructeur
public CameraHelper(SurfaceView cameraView) {
surfaceView.getHolder().addCallback(this);
}
// les 5 écouteurs ...
}
168
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
On s’intéresse aux capteurs qui indiquent l’orientation, c’est à dire une information sur la direction
dans laquelle est orientée la tablette par rapport au nord. Ça peut être un vecteur 3D orienté comme
la face supérieure de l’écran, un triplet d’angles, une matrice de rotation ou un quaternion.
• cap ou azimut (azimuth en anglais) = angle à plat donnant la direction par rapport au nord,
• tangage (pitch) = angle de bascule avant/arrière,
• roulis (roll) = angle d’inclinaison latérale.
Le problème de ces angles est le blocage de Cardan : quand le tangage vaut 90°, que signifie le cap ?
169
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Il n’y a aucune permission à demander pour les capteurs. Ils ne fournissent pas des informations
jugées sensibles.
...
rotationSensor = sensorManager.getDefaultSensor(
Sensor.TYPE_ROTATION_VECTOR);
accelerometerSensor = sensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER);
Le troisième paramètre est un code indiquant la périodicité souhaitée (très fréquente ou moins).
170
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
break;
case Sensor.TYPE_ACCELEROMETER:
... utiliser event.values en tant que déplacement
break;
}
}
Les données event.values sont un tableau de float qui dépend du capteur. Il y a un calcul spécifique
et la documentation n’est pas toujours assez précise.
171
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
La variable rotationMatrix est une matrice 4x4. Elle représente la rotation à appliquer sur un point
3D pour l’amener dans le repère du smartphone, donc exactement ce qu’il nous faut.
172
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
case Sensor.TYPE_ORIENTATION:
Matrix.setIdentityM(rotationMatrix, 0);
Matrix.rotateM(rotationMatrix, 0, event.values[1], 1,0,0);
Matrix.rotateM(rotationMatrix, 0, event.values[2], 0,1,0);
Matrix.rotateM(rotationMatrix, 0, event.values[0], 0,0,1);
9.6.2. Assemblage
Il faut regrouper plusieurs techniques :
• la caméra nous fournit l’image de fond.
• Une vue est superposée pour dessiner les icônes et textes des POIs. C’est une vue de dessin 2D
comme dans le cours précédent.
• Le GPS donne la position sur le globe terrestre permettant d’obtenir la direction relative des
POIs.
• Le capteur d’orientation permet de déterminer la position écran des POIs, s’ils sont visibles.
Le lien entre les trois derniers points se fait avec une matrice de transformation. Le but est de
transformer des coordonnées 3D absolues (sur le globe terrestre) en coordonnées 2D de pixels sur
l’écran.
173
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Il existe un repère global 3D attaché à la Terre. On l’appelle ECEF earth-centered, earth-fixed. C’est
un repère dont l’origine est le centre de la Terre, l’axe X passe par l’équateur et le méridien de
Greenwich, l’axe Y est 90° à l’est.
Connaissant le rayon de la Terre, et son excentricité (elle est aplatie), on peut transformer tout point
géographique en point 3D ECEF. Le calcul est complexe, hors de propos ici, voir cette page.
174
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Semaine 10
Le cours de cette semaine concerne le dessin de cartes géographiques en ligne. Il est lié au cours 7 et
en faisait partie auparavant.
On commence par les AsyncTasks qui sont nécessaires pour faire des calculs longs comme ceux de
l’affichage d’une carte, ou des requêtes réseau.
10.1. AsyncTasks
10.1.1. Présentation
Une activité Android repose sur une classe, ex MainActivity qui possède différentes méthodes comme
onCreate, les écouteurs des vues, des menus et des chargeurs.
Ces fonctions sont exécutées par un seul processus léger, un thread appelé « Main thread ». Il dort la
plupart du temps, et ce sont les événements qui le réveillent.
Ce thread ne doit jamais travailler plus de quelques fractions de secondes sinon l’interface paraît
bloquée et Android peut même décider que l’application est morte (App Not Responding).
Voir la figure 52, page 176.
175
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
176
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Ça veut dire qu’on peut appeler la même méthode de toutes ces manières, le nombre de paramètres
est variable :
doInBackground();
doInBackground("www.meteo.fr");
doInBackground("www.meteo.fr", "www.weather.fr","www.bericht.fr");
177
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
execute va créer un thread séparé pour effectuer doInBackground, mais les autres méthodes du
AsyncTask restent dans le thread principal.
178
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
10.1.14. Simplification
On peut simplifier un peu s’il n’y a pas besoin de ProgressBar et si le résultat est directement utilisé
dans onPostExecute :
179
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
return pluie;
}
protected void onPostExecute(Float pluie) {
// utiliser pluie, ex: l'afficher dans un TextView
...
}
}
10.1.16. Recommandations
Il faut faire extrêmement attention à :
• ne pas bloquer le thread principal dans une callback plus de quelques fractions de secondes,
• ne pas manipuler une vue ailleurs que dans le thread principal.
Ce dernier point est très difficile à respecter dans certains cas. Si on crée un thread, il ne doit jamais
accéder aux vues de l’interface. Un thread n’a donc aucun moyen direct d’interagir avec l’utilisateur.
Si vous tentez quand même, l’exception qui se produit est :
Only the original thread that created a view hierarchy can touch its views
Les solutions dépassent largement le cadre de ce cours et passent par exemple par la méthode
Activity.runOnUiThread
180
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Le handler gère le lancement immédiat (post) ou retardé (postDelayed) de la tâche. Elle peut
elle-même se relancer.
10.2. OpenStreetMap
10.2.1. Présentation
Au contraire de Google Maps, OSM est vraiment libre et OpenSource, et il se programme extrêmement
facilement.
Voir la figure 54, page 183.
10.2.2. Documentation
Nous allons utiliser deux librairies :
• OSMdroid : c’est la librarie de base, super mal documentée. Attention à ne pas confondre avec
un site de piraterie.
• OSMbonusPack, un ajout remarquable à cette base. Son auteur s’appelle Mathieu Kergall. Il
a ajouté de très nombreuses fonctionalités permettant entre autres d’utiliser OpenStreetMap
pour gérer des itinéraires comme les GPS de voiture et aussi afficher des fichiers KML venant
de Google Earth.
Lire cette suite de tutoriels pour découvrir les possibilités de osmbonuspack.
181
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
L’inclusion de librairies est à la fois simple et compliqué. La complexité vient de l’intégration des
librairies et de leurs dépendances dans un serveur central, « maven ».
@Override
protected void onCreate(Bundle savedInstanceState)
{
// mise en place de l'interface
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
183
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
mapController.setZoom(14);
mapController.setCenter(new GeoPoint(48.745, -3.455));
Un GeoPoint est un couple (latitude, longitude) représentant un point sur Terre. Il y a aussi l’altitude
si on veut. C’est équivalent à un LatLng de GoogleMaps.
10.2.7. Calques
Les ajouts sur la carte sont faits sur des overlays. Ce sont comme des calques. Pour ajouter quelque
chose, il faut créer un Overlay, lui rajouter des éléments et insérer cet overlay sur la carte.
Il existe différents types d’overlays, p. ex. :
• ScaleBarOverlay : rajoute une échelle
• ItemizedIconOverlay : rajoute des marqueurs
• RoadOverlay, Polyline : rajoute des lignes
Par exemple, pour rajouter un indicateur d’échelle de la carte :
// redessiner la carte
mMap.invalidate();
Ça marche sans cela dans la plupart des cas, mais y penser s’il y a un problème.
10.2.9. Marqueurs
Un marqueur est représenté par un Marker :
184
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
mrkIUT.setOnMarkerClickListener(new OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker, MapView map)
{
Toast.makeText(MainActivity.this,
marker.getSnippet(),
Toast.LENGTH_LONG).show();
return false;
}
});
10.2.12. Itinéraires
Il est très facile de dessiner un itinéraire sur OSM. On donne le GeoPoint de départ et celui d’arrivée
dans une liste, éventuellement des étapes intermédiaires :
185
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
LocationManager locationManager =
(LocationManager) getSystemService(LOCATION_SERVICE);
Location position =
locationManager.getLastKnownLocation(
LocationManager.GPS_PROVIDER);
if (position != null) {
mapController.setCenter(new GeoPoint(position));
}
NB: ça ne marche qu’en plein air (réception GPS). Consulter aussi cette page à propos de l’utilisation
du GPS et des réseaux.
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);
186
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
Il faut afficher la fenêtre Android Device Monitor par le menu Tools, item Android. Dans l’onglet
Emulator, il y a un panneau pour définir la position de l’AVD, soit fixe, soit à l’aide d’un fichier GPX
provenant d’un récepteur GPS de randonnée par exemple.
Cette fenêtre est également accessible avec le bouton ... en bas du panneau des outils de l’AVD.
mMap.getOverlays().add(new LongPressMapOverlay());
@Override
public boolean onLongPress(MotionEvent event, MapView map)
{
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Projection projection = map.getProjection();
GeoPoint position = (GeoPoint) projection.fromPixels(
(int)event.getX(), (int)event.getY());
// utiliser position ...
}
return true;
}
10.2.18. Autorisations
Pour finir, Il faut autoriser plusieurs choses dans le Manifeste : accès au GPS et au réseau, et écriture
sur la carte mémoire :
187
IUT de Lannion P. Nerzic
Dept Informatique Programmation Android 2020-21
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
188