Android - Semaine 6
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.
Android - Semaine 6
Plugin Lombok
Plugin Lombok
Android - Semaine 6
Plugin Lombok
Présentation
Le plugin Lombok, c’est son nom, regroupe un ensemble de
fonctions qui se déclenchent lors de la compilation d’un source Java.
Elles effectuent des transformations utiles sur le source.
Pour cela, on rajoute des annotations sur la classe ou sur les
variables membres.
Une annotation Java est un mot clé commençant par un @. Il
déclenche une méthode dans ce qu’on appelle un Annotation
Processor. La méthode fait certaines vérifications ou génère du
source Java d’après votre programme.
@Override, @SuppressWarnings("unused"), @Nullable,
@NonNull sont des annotations prédéfinies dans Java (package
android.support.annotation). Lombok en ajoute d’autres.
Android - Semaine 6
Plugin Lombok
Exemple
Voici comment générer automatiquement les setters et getters et la
méthode toString() :
import lombok.*;
@ToString @Setter @Getter
public class Personne
{
private int id;
private String nom;
private String prenom;
}
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="Moha", prenom="Ali").
Android - Semaine 6
Plugin Lombok
Placement des annotations
On peut placer les annotations @Setter et @Getter soit au niveau
de la classe, soit seulement sur certaines variables.
@ToString
@Setter
public class Personne
{
private int id;
@Getter private String nom;
@Getter private String prenom;
}
On aura un setter pour chaque variable et un getter seulement pour
le nom et le prénom.
6 / 55 Pierre Nerzic
Android - Semaine 6
Plugin Lombok
Nommage des champs
Lombok est adapté à un nommage simple des champs, en écriture
de chameaux lowerCamelCase et non pas en écriture hongroise. Il
génère les setters et getters en ajoutant set ou get devant le nom,
avec sa première lettre mise en majuscule.
private int nbProduits; génère
• public void setNbProduits(int n)
• public int getNbProduits()
Dans le cas des booléens, le getter commence par is
private boolean enVente; génère
• public setEnVente(boolean b)
• public boolean isEnVente()
7 / 55 Pierre Nerzic
Android - Semaine 6
Plugin Lombok
Installation du plugin
Voir cette page.
Il faut passer par les settings d’Android Studio, onglet Plugins, il
faut chercher Lombok Plugin dans la liste.
Ensuite, dans chaque projet, il faut rajouter quelques lignes
dans lefichier app/build.gradle :
compileOnly 'org.projectlombok:lombok:1.18.16'
annotationProcessor 'org.projectlombok:lombok:1.18.16'
testCompileOnly 'org.projectlombok:lombok:1.18.16'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.16'
8 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Realm
9 / 55 Pierre Nerzic
Android - Semaine 6
Realm
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.
Il est très bien expliqué sur ces pages. Ce cours suit une partie de
cette documentation.
10 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Configuration d’un projet Android avec Realm
Avant toute chose, quand on utilise Realm, il faut éditer les deux
fichiers build.gradle :
celui du projet, ajouter sous l’autre classpath :
classpath 'io.realm:realm-gradle-plugin:3.3.2'
celui du dossier app :
apply plugin: 'realm-android'
android {
...
}
dependencies {
implementation 'io.realm:android-adapters:2.1.0'
...
11 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Initialisation d’un Realm
Vous devez placer ces instructions dans la méthode onCreate d’une
sous-classe d’Application associée à votre logiciel :
import io.realm.Realm;
public class MyApplication extends Application
{
@Override
public void onCreate()
{
super.onCreate();
Realm.init(this);
}
}
avec <application android:name=".MyApplication"... dans
le manifeste.
12 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Ouverture d’un Realm
Ensuite, dans chaque activité, on rajoute ceci :
public class MainActivity extends Activity
{
private Realm realm;
@Override
public void onCreate()
{
super.onCreate();
...
realm = Realm.getDefaultInstance();
...
}
}
13 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Fermeture du Realm
Il faut également fermer le Realm à la fin de l’activité :
public class MainActivity extends Activity
{
...
@Override
public void onDestroy()
{
super.onDestroy();
realm.close();
}
}
14 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Autres modes d’ouverture du Realm
La méthode précédente ouvre un Realm local au smartphone. Les
données seront persistantes d’un lancement à l’autre, mais elles ne
seront pas enregistrées à distance.
Il existe également des Realm distants, sur un serveur appelé Realm
Object Server. On les appelle des Realms synchronisés. Cela impose
l’installation d’un tel serveur ainsi que l’établissement d’une
connexion et d’une authentification de l’utilisateur. Ce n’est pas
compliqué, mais hors propos de ce cours, voir cette page.
Il existe également des Realms en mémoire, pratiques pour faire des
essais. L’ouverture se fait ainsi :
RealmConfiguration conf =
new RealmConfiguration.Builder().inMemory().build();
realm = Realm.getInstance(conf);
15 / 55 Pierre Nerzic
Android - Semaine 6
Realm
Migration des données
Autre point crucial à savoir : quand vous changez le modèle des
données, il faut prévoir une procédure de migration des données.
C’est à dire une sorte de recopie et d’adaptation au nouveau modèle.
Cette migration est automatique quand vous ne faites qu’ajouter de
nouveaux champs aux objets, mais vous devez la programmer si
vous supprimez ou renommez des variables membres.
16 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Modèles de données Realm
17 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Définir une table
Pour cela, il suffit de faire dériver une classe de la superclasse
RealmObject :
import io.realm.RealmObject;
public class Produit extends RealmObject
{
private int id;
private String designation;
private float prixUnitaire;
}
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.
18 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Table Realm et Lombok
On peut employer le plugin Lombok sur une classe :
@ToString
@Setter
@Getter
public class Produit extends RealmObject
{
private int id;
private String designation;
private float prixUnitaire;
}
Elle reste donc extrêmement lisible. On peut se concentrer sur les
algorithmes.
NB: dans la suite, l’emploi du plugin Lombok sera implicite.
19 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Types des colonnes
Realm gère tous les types Java de base : boolean, byte, short,
int, long, float, double, String, Date et byte[].
Il faut savoir qu’en interne, tous les entiers byte, short, int, long
sont codés en interne par un long.
Il y a aussi tous les types Java emballés (boxed types) : Boolean,
Byte, Short, Integer, Long, Float et Double. Ceux-là peuvent
tous avoir la valeur null.
20 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Empêcher les valeurs null
Si une variable objet ne doit pas être indéfinie, rajoutez l’annotation
@Required :
import io.realm.annotations.Required;
public class Produit extends RealmObject
{
private int id;
@Required private String designation;
private float prixUnitaire;
}
Si vous tentez d’affecter designation avec null, ça déclenchera
une exception.
21 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Définir une clé primaire
Dans l’exemple précédent, il y a une variable d’instance id qui est
censée contenir un entier unique. Pour le garantir dans Realm, il
faut l’annoter avec @PrimaryKey :
import io.realm.annotations.PrimaryKey;
public class Produit extends RealmObject
{
@PrimaryKey private long id;
private String designation;
private float prixUnitaire;
}
Si vous tentez d’affecter à id la même valeur pour deux achats
différents, vous aurez une exception.
22 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Définir une relation simple
En Realm, une relation est simplement une référence sur un autre
objet Realm :
public class Achat extends RealmObject
{
private Produit produit;
private Date date;
private int nombre;
}
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.
23 / 55 Pierre Nerzic
Android - Semaine 6
Modèles de données Realm
Relation multiple
Dans le transparent précédent, un Achat n’est associé qu’à un seul
Produit. Si on en veut plusieurs, il faut utiliser une collection
RealmList<Type> :
public class Achat extends RealmObject
{
private RealmList<Produit> produits;
private Date date;
}
Le champ produit est relié à plusieurs produits.
Le type RealmList possède les méthodes des List et Iterable
entre autres : add, clear, get, contains, size. . .
24 / 55 Pierre Nerzic
Android - Semaine 6
Création de n-uplets
Création de n-uplets
25 / 55 Pierre Nerzic
Android - Semaine 6
Création de n-uplets
Résumé
Un n-uplet est simplement une instance d’une classe qui hérite de
RealmObject.
Cependant, les variables Java ne sont pas enregistrées de manière
permanente dans Realm. Il faut :
soit créer les instances avec une méthode de fabrique de leur
classe,
soit créer les instances avec new puis les ajouter dans Realm.
Dans tous les cas, il faut initialiser un Realm au début de
l’application.
26 / 55 Pierre Nerzic
Android - Semaine 6
Création de n-uplets
Création de n-uplets par createObject
Une fois le realm ouvert, voici comment créer des instances. Dans
Realm, toute création ou modification de données doit être faite
dans le cadre d’une transaction, comme dans SQL :
realm.beginTransaction();
Produit produit = realm.createObject(Produit.class, 1L);
produit.setDesignation("HP840");
produit.setPrixUnitaire(7500);
realm.commitTransaction();
Le second paramètre de createObject est l’identifiant, obligatoire
s’il y a une @PrimaryKey.
Si vous oubliez de placer les modifications dans une transaction,
vous aurez une IllegalStateException.
27 / 55 Pierre Nerzic
Android - Semaine 6
Création de n-uplets
Création de n-uplets par new
Une autre façon de créer des n-uplets consiste à utiliser new puis à
enregistrer l’instance dans realm :
Produit produit = new Produit();
produit.setId(1L);
produit.setDesignation("brosse à dent");
produit.setPrixUnitaire(4.95);
realm.beginTransaction();
Produit realmProd = realm.copyToRealm(produit);
realm.commitTransaction();
Le problème est que ça crée deux objets, seul le dernier est connu
de Realm.
28 / 55 Pierre Nerzic
Android - Semaine 6
Création de n-uplets
Modification d’un n-uplet
Là également, il faut placer tous les changements dans une
transaction :
realm.beginTransaction();
produit.setDesignation("Asus787");
produit.setPrixUnitaire(9500);
realm.commitTransaction();
29 / 55 Pierre Nerzic
Android - Semaine 6
Création de n-uplets
Suppression de n-uplets
On ne peut supprimer que des objets gérés par realm, issus de
createObject ou copyToRealm :
realm.beginTransaction();
produit.deleteFromRealm();
realm.commitTransaction();
Il est possible de supprimer plusieurs objets, en faisant appel à une
requête.
30 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Requêtes sur la base
31 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Résumé
Une requête Realm retourne une collection d’objets (tous
appartenant à Realm) sous la forme d’un RealmResults<Type>.
Ce qui est absolument magique, c’est que cette collection est
automatiquement mise à jour en cas de changement des données,
voir auto-updating results.
Par exemple, si vous utilisez un Realm distant, tous les changements
opérés ailleurs vous seront transmis. Ou si vous avez deux
fragments, une liste et un formulaire d’édition : tous les
changements faits par le formulaire seront répercutés dans la liste
sans aucun effort de votre part.
Les RealmResults ne sont donc pas comme de simples ArrayList.
Ils sont vivants.
32 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Sélections
La requête la plus simple consiste à récupérer la liste de tous les
n-uplets d’une table :
RealmResults<Produit> results = realm
.where(Produit.class)
.findAll();
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.
33 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Conditions simples
Comme avec SQL, on peut rajouter des conditions :
RealmResults<Produit> results = realm
.where(Produit.class)
.equalTo("designation", "hp840")
.findAll();
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
34 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Nommage des colonnes
Cet exemple montre le gros défaut, à mon sens, de Realm : on doit
nommer les champs à l’aide de chaînes. Si on se trompe sur le nom
de champ, ça cause une exception.
Il est très conseillé de faire ceci :
public class Produit extends RealmObject
{
public static final String FIELD_ID = "id";
public static final String FIELD_DESIGNATION = "designation"
...
private int id;
private String designation;
...
35 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Librairie RealmFieldsHelper
La page RealmFieldsHelper décrit une bibliothèque très utile qui fait
ce travail automatiquement. Il faut juste rajouter ceci dans la partie
dépendances du app/build.gradle :
annotationProcessor 'dk.ilios:realmfieldnameshelper:1.1.1'
Pour chaque CLASSE de type RealmObject, ça génère
automatiquement une classe appelée CLASSEFields contenant des
chaînes constantes statiques valant les noms des champs de la
classe, et les champs des objets liés sont également nommés :
RealmResults<Achat> results = realm
.where(Achat.class)
.equalTo(AchatFields.PRODUIT.DESIGNATION, "dentifrice")
.greaterThan(AchatFields.NOMBRE, 5)
.findAll();
36 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Conjonctions de conditions
Une succession de conditions réalise une conjonction :
RealmResults<Produit> results = realm
.where(Produit.class)
.notEqualTo(Produit.FIELD_ID, 0)
.equalTo(Produit.FIELD_DESIGNATION, "dentifrice")
.lessThanOrEqualTo(Produit.FIELD_PRIX, 3.50)
.findAll();
ou bien, si on utilise realmfieldshelper :
RealmResults<Produit> results = realm
.where(Produit.class)
.notEqualTo(ProduitFields.ID, 0)
.equalTo(ProduitFields.DESIGNATION, "dentifrice")
.lessThanOrEqualTo(ProduitFields.PRIX, 3.50)
.findAll();
37 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Disjonctions de conditions
Voici un exemple de disjonction avec beginGroup et endGroup qui
jouent le rôle de parenthèses :
RealmResults<Produit> results = realm
.where(Produit.class)
.beginGroup()
.equalTo(Produit.FIELD_DESIGNATION, "dentifrice")
.lessThanOrEqualTo(Produit.FIELD_PRIX, 3.50)
.endGroup()
.or()
.beginGroup()
.equalTo(Produit.FIELD_DESIGNATION, "fil dentaire")
.lessThanOrEqualTo(Produit.FIELD_PRIX, 8.50)
.endGroup()
.findAll();
38 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Négations
La méthode not() permet d’appliquer une négation sur la condition
qui la suit :
RealmResults<Produit> results = realm
.where(Produit.class)
.not()
.beginGroup()
.equalTo(Produit.FIELD_DESIGNATION, "dentifrice")
.or()
.equalTo(Produit.FIELD_DESIGNATION, "fil dentaire")
.endGroup()
.findAll();
On peut évidemment la traduire en conjonction de conditions
opposées.
39 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Classement des données
On peut trier sur l’un des champs :
RealmResults<Produit> results = realm
.where(Produit.class)
.sort(Produit.FIELD_PRIX, Sort.DESCENDING)
.findAll();
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.
40 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Agrégation des résultats
La méthode findAll() retourne tous les objets vérifiant ce qui
précède. C’est en fait cette méthode qui retourne le RealmResults.
On peut aussi utiliser findFirst() pour n’avoir que le premier.
Dans ce cas, on ne récupère pas un RealmResults mais un seul
objet.
Pour les champs numériques, il existe aussi min(nom_champ),
max(nom_champ) et sum(nom_champ) qui retournent seulement
une valeur du même type que le champ.
41 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Jointures 1-1
On arrive au plus intéressant, mais hélas frustrant comparé à ce qui
est possible avec SQL. On reprend cette classe :
public class Achat extends RealmObject {
private Produit produit;
private Date date;
}
On utilise une notation pointée pour suivre la référence :
RealmResults<Achat> achats_dentif = realm
.where(Achat.class)
.equalTo("produit.designation", "hp840")
.findAll();
Mieux : realmfieldhelper AchatField.PRODUIT.DESIGNATION ou
Achat.FIELD_PRODUIT+"."+Produit.FIELD_DESIGNATION
42 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Jointures 1-N
Cela marche aussi avec cette classe :
public class Achat extends RealmObject {
private RealmList<Produit> produits;
private Date date;
}
Cette requête va chercher parmi tous les produits liés et retourne les
achats qui sont liés à au moins un produit dentifrice :
RealmResults<Achat> achats_dentif = realm
.where(Achat.class)
.equalTo(AchatField.PRODUITS.DESIGNATION, "dentifrice")
.findAll();
Mais impossible de chercher les achats qui ne concernent que des
dentifrices ou tous les dentifrices.
43 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Jointures inverses
Avec les jointures précédentes, on peut aller d’un Achat à un
Produit. Realm propose un mécanisme appelé relation inverse pour
connaître tous les achats contenant un produit donné :
public class Achat extends RealmObject {
private RealmList<Produit> produits;
private Date date;
}
public class Produit extends RealmObject {
private String designation;
private float prix;
@LinkingObjects("produits")
private final RealmResults<Achat> achats = null;
}
44 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Jointures inverses, explications
Pour lier une classe CLASSE2 à une CLASSE1, telles que l’un des
champs de CLASSE1 est du type CLASSE2 ou liste de CLASSE2, il
faut modifier CLASSE2 :
définir une variable finale valant null du type
RealmList<CLASSE1> ; elle sera automatiquement réaffectée
lors de l’exécution
lui rajouter une annotation @LinkingObjects("CHAMP") avec
CHAMP étant le nom du champ concerné dans CLASSE1.
Dans l’exemple précédent, le champ achats de Produit sera rempli
dynamiquement par tous les Achat contenant le produit considéré.
45 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Jointures inverses, exemple
Exemple de requête :
Produit dentifrice = realm
.where(Produit.class)
.equalTo(ProduitFields.DESIGNATION, "dentifrice")
.findFirst();
RealmResults<Achats> achats_dentifrice = dentifrice.getAchats();
Ceci en utilisant Lombok pour créer le getter sur le champ achats.
46 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes sur la base
Suppression par une requête
Le résultat d’une requête peut servir à supprimer des n-uplets. Il
faut que ce soit fait dans le cadre d’une transaction et attention à
ne pas supprimer des données référencées dans d’autres objets :
realm.beginTransaction();
achats_dentif.deleteAllFromRealm();
dentifrices.deleteAllFromRealm();
realm.commitTransaction();
47 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Requêtes et adaptateurs de listes
48 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Adaptateur Realm pour un RecyclerView
Realm simplifie énormément l’affichage des listes, voir le cours 4
pour les notions. Soit par exemple un RecyclerView. Le problème
essentiel est la création de l’adaptateur. Il y a une classe Realm
pour cela, voici un exemple :
public class ProduitAdapter
extends RealmRecyclerViewAdapter<Produit, ProduitView>
{
public ProduitAdapter(RealmResults<Produit> produits)
{
super(produits, true);
}
Le booléen true à fournir à la superclasse indique que les données
se mettent à jour automatiquement.
49 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Adaptateur Realm, suite
Ensuite, il faut programmer la méthode de création des vues :
@Override
public ProduitView onCreateViewHolder(
ViewGroup parent, int viewType)
{
View itemView = LayoutInflater
.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new ProduitView(itemView);
}
Pas de changements a ce niveau !!
50 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Adaptateur Realm, suite
Ensuite, il faut programmer la méthode qui place les informations
dans les vues :
@Override
public void onBindViewHolder(
ProduitView holder, final int position)
{
// afficher les informations du produit
final Produit produit = getItem(position);
holder.setItem(produit);
}
Pas de changements a ce niveau aussi !!
51 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Adaptateur Realm, fin
Dans le cas d’une classe avec identifiant, il faut rajouter une
dernière méthode :
@Override
public long getItemId(int position)
{
return getItem(position).getId();
}
Elle est utilisée pour obtenir l’identifiant de l’objet cliqué dans la
liste, voir onItemClick plus loin.
52 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Mise en place de la liste
Dans la méthode onCreate de l’activité qui gère la liste :
@Override
protected void onCreate(Bundle savedInstanceState)
{
...
Realm realm = Realm.getDefaultInstance();
RealmResults<Produit> liste =
realm.where(Produit.class).findAll();
RecyclerView.Adapter adapter = new ProduitAdapter(liste);
recyclerView.setAdapter(adapter);
}
Comme la liste est vivante, toutes les modifications seront
automatiquement reportées à l’écran sans qu’on ait davantage de
choses à programmer.
53 / 55 Pierre Nerzic
Android - Semaine 6
Requêtes et adaptateurs de listes
Réponses aux clics sur la liste
L’écouteur de la liste doit transformer la position du clic en
identifiant :
@Override
public void onItemClick(
AdapterView<?> parent, View v, int position, long id)
{
long identifiant = adapter.getItemId(position);
...
}
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.
54 / 55 Pierre Nerzic