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.
- Que sont les collections observables ?
- Comment créer une nouvelle instance d'une collection observable ?
- Comment convertir une collection standard en collection observable ?
- Comment recevoir les notifications de modification d'une liste observable ?
- Comment traiter les notifications de modification d'une liste observable ?
- Comment créer une liste observable automatiquement triée ?
- Comment créer une liste observable automatiquement filtrée ?
- Comment recevoir les notifications de modification d'une table de hachage observable ?
- Comment traiter les notifications de modification d'une table de hachage observable ?
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).
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.
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.
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.
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é |
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é. |
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é. |
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 |
À 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.
À 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.
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.
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é. |
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é. |
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é. |
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 çaLes 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.