IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo

FAQ JavaFXConsultez toutes les FAQ

Nombre d'auteurs : 4, nombre de questions : 507, dernière mise à jour : 2 novembre 2016  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur le forum JavaFX de http://java.developpez.com ainsi que l'expérience personnelle des auteurs.

Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.

SommaireCollections observables (9)
précédent sommaire suivant
 

JavaFX introduit une nouvelle API de collections : les collections observables. Ces collections émettent des événements quand leur contenu change, que des objets sont ajoutés, retirés ou modifiés dans la collection. Les classes de cette API sont situées dans le package javafx.collections.

Il existe quatre types de collections observables fournis par l'API JavaFX :

  • ObservableArray - une classe observable qui encapsule des tableaux d'entiers ou de flottants - JavaFX 8 ou ultérieur ;
  • ObservableList<T> - les listes observables ;
  • ObservableMap<T> - les tables de hachage/dictionnaires observables ;
  • ObservableSet<T> - les ensembles observables.


L'API SceneGraph utilise la classe ObservableList de manière intensive ; il est donc conseillé de s'intéresser à leur fonctionnement pour mieux comprendre le fonctionnement des gestionnaires de mise en page, des listes, arbres et tables graphiques, etc.

À l'exception d'ObservableArray qui lui est propre, cette API reste compatible avec l'API standard de collections de Java ainsi qu'avec toutes les améliorations introduites dans le JDK8 (streams).

Mis à jour le 10 septembre 2014 bouye

La classe utilitaire javafx.collections.FXCollections contient des méthodes permettant de créer de nouvelles collections observables.

Par exemple :

Code Java : Sélectionner tout
final ObservableList<Person> people = FXCollections.observableArrayList();

Ce code créera une nouvelle liste observable vide.

Il est également possible de fournir des éléments permettant d’initialiser le contenu de la liste :

Code Java : Sélectionner tout
final ObservableList<Person> people = FXCollections.observableArrayList(person1, person2, person3);

En interne, la liste est stockée en utilisant une instance de la classe java.util.ArrayList de l'API de collection standard Java.

Mis à jour le 10 septembre 2014 bouye

La classe utilitaire javafx.collections.FXCollections contient des méthodes permettant d'empaqueter une collection Java standard en collection observable.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
final Map<<PhoneNumber, Person> phoneBook = new HashMap<>(); 
// Remplissage de l'annuaire. 
[...] 
final ObservableMap<PhoneNumber, Person> phoneBookObservable = FXCollections.observableMap(phoneBook);

Ici, notre table de hachage est désormais empaquetée dans une table de hachage observable.

De la même manière, il est possible de créer des listes observables utilisant une instance de LinkedList comme stockage interne au lieu d'une instance de ArrayList. Par exemple :

Code Java : Sélectionner tout
final ObservableList<Car> carList = FXCollections.observableList(new LinkedList());

Note : il va de soi que pour pouvoir émettre correctement des événements en cas de modification, ce sont les versions observables des collections qui doivent être désormais manipulées. Toute manipulation de la version non observable de la collection passera inaperçue.

Mis à jour le 10 septembre 2014 bouye

Pour recevoir les notifications de modification d'une ObservableList<T>, il faut lui ajouter un écouteur de type javafx.collections.ListChangeListener<T> et traiter l'objet de type javafx.collections.ListChangeListener.Change<? extends T> passé en paramètre de sa méthode onChanged().

Commençons par créer une liste observable :

Code Java : Sélectionner tout
final ObservableList<String> names = FXCollections.observableArrayList("André", "Élodie", "Jasmina", "Malik", "Valérie", "Yvana");

Maintenant, nous allons lui ajouter un écouteur de type ListChangeListener:

Code Java : Sélectionner tout
1
2
3
4
5
6
7
names.addListener(new ListChangeListener<String>() { 
  
    @Override 
    public void onChanged(ListChangeListener.Change<? extends String> change) { 
        // Traitement des changements ici. 
    }            
});

Nous pouvons également faire la même chose en utilisant les expressions lambda du JDK8 :

Code Java : Sélectionner tout
1
2
3
names.addListener((ListChangeListener.Change<? extends String> change) -> {            
    // Traitement des changements ici. 
});

Ici, il est nécessaire de spécifier le type de l'argument de l'expression lambda, car il existe également une variante de la méthode addListener() acceptant un InvalidationListener dans la classe ObservableList. Spécifier le type permet de résoudre l’ambigüité sur la méthode qui doit être invoquée.

Mis à jour le 10 septembre 2014 bouye

Pour améliorer les performances et minimiser le nombre d’événements publiés, l'objet change reçu dans la méthode onChanged() d'un ListChangeListener est en fait un rapport agrégé contenant les récentes modifications sur la liste observable. Les diverses modifications devront être traitées dans une boucle de style :

Code Java : Sélectionner tout
1
2
3
while(change.next()) { 
   // Traiter chaque changement séparément ici. 
}

Il ne faut jamais invoquer de méthodes de l'objet change sans avoir invoqué sa méthode next() au préalable !

Dans un objet de type ListChangeListener.Change, nous pouvons recevoir cinq types de notifications :

  • Remplacement : une ou plusieurs valeurs ont été remplacées par d'autres ;
  • Permutation: une ou plusieurs valeurs ont échangé leur place dans la liste avec d'autres (qui étaient également dans la liste) ;
  • Mise à jour : une ou plusieurs valeurs ont été modifiées. Ce type de notification est optionnel et peut ne pas être pris en charge par tous les types de listes observables ;
  • Ajout : une ou plusieurs valeurs ont été ajoutées dans la liste ;
  • Suppression : une ou plusieurs valeurs ont été supprimées dans la liste.


Ces types ne sont pas exclusifs : plusieurs types peuvent être actifs en même temps.

Ajoutons l’écouteur suivant sur notre liste de noms :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
names.addListener((ListChangeListener.Change<? extends String> change) -> { 
    System.out.println("+Notification"); 
    while (change.next()) { 
        if (change.wasReplaced()) { 
            System.out.printf("++Remplacement %d %d", change.getFrom(), change.getTo()).println(); 
        } 
        if (change.wasPermutated()) { 
            System.out.printf("++Permutation %d %d", change.getFrom(), change.getTo()).println(); 
            for (int index = change.getFrom() ; index < change.getTo() ; index++) { 
                System.out.printf("%d -> %d", index, change.getPermutation(index)).println(); 
            } 
        } 
        if (change.wasUpdated()) { 
            System.out.printf("++Mise à jour %d %d", change.getFrom(), change.getTo()).println(); 
        } 
        if (change.wasAdded()) { 
            System.out.println("++Ajout"); 
            change.getAddedSubList().forEach(value -> System.out.printf("\"%s\" a été ajouté.", value).println()); 
        } 
        if (change.wasRemoved()) { 
            System.out.println("++Retrait"); 
            change.getRemoved().forEach(value -> System.out.printf("\"%s\" a été supprimé.", value).println()); 
        } 
    } 
    System.out.println(); 
});

Ajoutons maintenant quelques noms dans la liste :

Code Java : Sélectionner tout
names.addAll("Henri", "Oscar", "Manon");

La sortie de cette opération est :

Code : Sélectionner tout
1
2
3
4
5
+Notification 
++Ajout 
"Henri" a été ajouté. 
"Oscar" a été ajouté. 
"Manon" a été ajouté
Nous avons eu une notification d'ajout lors de cet événement. La méthode getAddedSubList() permet de retrouver toutes les valeurs qui ont été ajoutées dans la liste. Cette méthode retourne une vue sur la liste interne.

Retirons maintenant des éléments de la liste :

Code Java : Sélectionner tout
names.removeAll("André", "Élodie");

La sortie de cette opération est :

Code : Sélectionner tout
1
2
3
4
+Notification 
++Retrait 
"André" a été supprimé. 
"Élodie" a été supprimé.
Nous avons eu une notification de retrait lors de cet événement. La méthode getRemoved() permet de retrouver toutes les valeurs qui ont été retirées dans la liste.

Remplaçons maintenant un élément de la liste par un autre :

Code Java : Sélectionner tout
names.set(0, "Barbara");

La sortie de cette opération est :

Code : Sélectionner tout
1
2
3
4
5
6
+Notification 
++Remplacement 0 1 
++Ajout 
"Barbara" a été ajouté. 
++Retrait 
"Jasmina" a été supprimé.
Ici, nous avons non seulement un événement de remplacement ("Barbara" remplace "Jasmina"), mais également un événement d'ajout ("Barbara" a été ajouté dans la liste) et de retrait ("Jasmina" a été retiré de la liste). Nous savons également que la modification impacte l'intervalle d'indices [0, 1[, donc seule la première valeur de la liste a été modifiée.

Nous allons maintenant trier la liste :

Code Java : Sélectionner tout
FXCollections.sort(names);

La sortie de cette opération est :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
+Notification 
++Permutation 0 7 
0 -> 0 
1 -> 2 
2 -> 5 
3 -> 6 
4 -> 1 
5 -> 4 
6 -> 3
Nous savons que la permutation a affecté l'ensemble des éléments compris entre les indices [0, 7[ de la liste et nous pouvons connaitre le nouvel indice de chaque valeur en appelant la méthode getPermutation() de l'objet change et en passant son ancien indice en paramètre.

Mis à jour le 10 septembre 2014 bouye

À partir du JDK 8, il est possible de créer une liste observable automatiquement triée en empaquetant une liste observable source dans une instance de la classe javafx.collections.transformation.SortedList<T>.

Par exemple :

Code Java : Sélectionner tout
1
2
final ObservableList<String> names = FXCollections.observableArrayList("Yvana", "Élodie", "André", "Jasmina", "Malik", "Valérie"); 
final SortedList<String> sortedNames = new SortedList(names);

Ici le contenu de la nouvelle liste n'est initialement pas trié et adopte donc le même ordre d’éléments que dans la liste source.

Il est possible de changer l'ordre ou le critère du tri en spécifiant une instance de la classe java.util.Comparator dans la propriété comparator de la liste triée :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
sortedNames.setComparator(new Comparator<String>(){ 
  
    @Override 
    public int compare(String o1, String o2) { 
        return o1.compareTo(o2); 
    }             
});

Ou :

Code Java : Sélectionner tout
sortedNames.setComparator(String::compareTo);


Ce qui nous donne :

Code : Sélectionner tout
[André, Jasmina, Malik, Valérie, Yvana, Élodie]

Ici, la liste triée contient donc les mêmes éléments que la liste source, mais triés dans un ordre lexicographique.

La liste sortedNames sera toujours triée, même si on ajoute, retire ou permute des éléments de la liste names. Tout changement sur la liste source est automatiquement répercuté sur la liste triée. La liste triée étant elle-même une liste observable, elle émettra les événements appropriés en cas de modifications de la liste source qui ont des répercussions sur son contenu.

Mis à jour le 8 octobre 2014 bouye

À partir du JDK 8, il est possible de créer une liste observable automatiquement filtrée en empaquetant une liste observable dans une instance de la classe javafx.collections.transformation.FilteredList<T>.

Par exemple :

Code Java : Sélectionner tout
1
2
final ObservableList<String> names = FXCollections.observableArrayList("Yvana", "Élodie", "André", "Jasmina", "Malik", "Valérie"); 
final FilteredList<String> filteredNames = new FilteredList(names);

Ici, la liste filteredNames est initialement vide, car l'absence de filtre fait qu'elle rejette tous les éléments de la liste source names.

Il est possible de spécifier une instance de la classe java.util.function.Predicate dans la propriété predicate de la liste filtrée pour rendre le filtrage opérationnel :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
filteredNames.setPredicate(new Predicate<String>() { 
  
    @Override 
    public boolean test(String value) { 
        return value.length() > 5; 
    } 
});

Ou :

Code Java : Sélectionner tout
filteredNames.setPredicate(value -> value.length() > 5);

Ce qui nous donne :

Code : Sélectionner tout
[Élodie, Jasmina, Valérie]

Ici, seuls les noms dont la longueur dépasse cinq caractères sont membres de la liste filtrée.

La liste filteredNames sera toujours automatiquement filtrée, même si on ajoute, retire ou permute des éléments de la liste names. Tout changement sur la liste source est automatiquement répercuté sur la liste filtrée. La liste filtrée étant elle-même une liste observable, elle émettra les événements appropriés en cas de modifications de la liste source qui ont des répercussions sur son contenu.

Mis à jour le 8 octobre 2014 bouye

Pour recevoir les notifications de modification d'une ObservableMap<K, V>, il faut lui ajouter un écouteur de type javafx.collections.MapChangeListener<K, V> et traiter l'objet de type javafx.collections.MapChangeListener.Change<? extends K, ? extends V> passé en paramètre de sa méthode onChanged().

Commençons par créer une liste observable :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
final Map<Integer, String> source = new HashMap(); 
source.put(0, "Anthony"); 
source.put(1, "Antoinette"); 
source.put(2, "Jérôme"); 
source.put(3, "Karine"); 
source.put(4, "Patrick"); 
final ObservableMap<Integer, String> users = FXCollections.observableMap(source);

Maintenant, nous allons lui ajouter un écouteur de type ListChangeListener :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
users.addListener(new MapChangeListener<Integer, String>() { 
  
    @Override 
    public void onChanged(MapChangeListener.Change<? extends Integer, ? extends String> change) { 
        // Traitement des changements ici. 
    } 
});

Nous pouvons également faire la même chose en utilisant les expressions lambda du JDK8 :

Code Java : Sélectionner tout
1
2
3
users.addListener((MapChangeListener.Change<? extends Integer, ? extends String> change) -> { 
    // Traitement des changements ici. 
});

Ici, il est nécessaire de spécifier le type de l'argument de l'expression lambda, car il existe également une variante de la méthode addListener() acceptant un InvalidationListener dans la classe MapList. Spécifier le type permet de résoudre l’ambigüité sur la méthode qui doit être invoquée.

Mis à jour le 10 septembre 2014 bouye

La gestion des événements sur une table de hachage observable est moins évoluée que sur les listes observables. Ici l'objet change reçu dans la méthode onChanged() d'un MapChangeListener contient une seule notification d’événement :

Dans un objet de type MapChangeListener.Change, nous pouvons recevoir deux types de notifications :

  • Ajout : une valeur a été ajoutée dans la table ;
  • Suppression : une valeur a été retirée de la table.


Ces types ne sont pas exclusifs : plusieurs types peuvent être actifs en même temps.

Ajoutons l’écouteur suivant sur notre table d'utilisateurs :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
users.addListener((MapChangeListener.Change<? extends Integer, ? extends String> change) -> { 
    System.out.println("+Notification"); 
    if (change.wasAdded()) {      
        System.out.printf("\"%s\" a été ajouté.", change.getValueAdded()).println(); 
    } 
    if (change.wasRemoved()) {                 
        System.out.printf("\"%s\" a été supprimé.", change.getValueRemoved()).println(); 
    } 
    System.out.println(); 
});

Ajoutons maintenant quelques noms dans la table :

Code Java : Sélectionner tout
1
2
users.put(5, "Renée"); 
users.put(6, "Hervé");

La sortie de ces opérations est :

Code : Sélectionner tout
1
2
3
4
5
+Notification 
"Renée" a été ajouté. 
 
+Notification 
"Hervé" a été ajouté.
Ici, nous avons reçu une notification par ajout de valeur.

Retirons maintenant des éléments de table :

Code Java : Sélectionner tout
1
2
users.remove(0); 
users.remove(1);

La sortie de cette opération est :

Code : Sélectionner tout
1
2
3
4
5
+Notification 
"Anthony" a été supprimé. 
 
+Notification 
"Antoinette" a été supprimé.
Ici, nous avons également reçu une seule notification par retrait.

Remplaçons maintenant un élément de la table par un autre :

Code Java : Sélectionner tout
users.put(3, "Annie");

La sortie de cette opération est :

Code : Sélectionner tout
1
2
3
+Notification 
"Annie" a été ajouté. 
"Karine" a été supprimé.
Ici, nous avons reçu un événement d'ajout ("Annie" a été ajouté dans la table) et un événement de retrait ("Karine" a été retiré de la table).

Mis à jour le 10 septembre 2014 bouye

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.