L'objectif de ce billet est de faire un petit résumé riche en exemples des différentes notions que j'ai eu à utiliser en faisant joujou avec les ListView de javafx. J'ai commencé par une utilisation basique (affichage d'une bête liste statique) ; puis mes besoins sont allés crescendo. Cet article suivra la même chronologie que celle qui a été la mienne durant mon apprentissage.
Pour commencer
Tous les exemples de code seront récupérables sur ce dépôt git :
git@github.com:beynet/listview.git
Pour exécuter le code démontré, importez le projet dans votre IDE (projet maven) ; la classe à exécuter est "ListViews".
Etape 1 - Une liste toute simple
Positionnement dans le code
Avec javafx, créer une listview et y rajouter/supprimer des éléments est très simple.
C'est ce que nous allons faire durant cette étape.
git checkout step1
Le code
Pour créer la liste :
ListView<Person> persons = new ListView<Person>();
Le code permettant de créer et ajouter des éléments à la liste est celui-ci (lignes 49 et 50 de la classe ListViews).
persons.getItems().add(new Person("John","Doe"));
persons.getItems().add(new Person("MyNameIs","Person"));
Ce qui donne :
Les personnes s'affichent correctement dans la liste car la classe Person surcharge la méthode toString() :
@Override
public String toString() {
return getFirstName()+" "+getLastName();
};
Par défaut, notre listview a comme modèle de données une liste de type ObservableList<Person> ; les modifications apportées à cette liste (ajout et suppression) sont directement reportées dans la vue graphique. C'est ce qu'illustre notre bouton addPerson : il fait apparaitre une boite de dialogue permettant de rajouter une nouvelle personne (saisissez un prénom et un nom séparés d'un espace) :
Après appuie sur la touche ok la liste est mise à jour. Le code qui met la liste à jour est le suivant ( sur activation du bouton OK ) :
Button addPerson = new Button("aj. person");
addPerson.setOnAction(event -> {
TextInputDialog askForName = new TextInputDialog();
askForName.setContentText("new person name");
Optional<string> result=askForName.showAndWait();
result.ifPresent((name)->{
String[] names=name.split(" ");
if (names.length==2) {
persons.getItems().add(new Person(names[0],names[1]));
}
});
});
Ce qui donne :
Etape 2 - Customiser l'affichage de la liste
Lors de cette étape nous allons voir comment customiser l'affichage des éléments de la liste.
Positionnement à l'étape 2
git checkout step2
Le code
Pour illustrer la customisation nous allons afficher le prénom en rouge et le nom en bleu.
Pour customiser l'affichage il faut implémenter une classe dérivant de ListCell. C'est notre classe PersonCell :
public class PersonCell extends ListCell<person> {
@Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
setText(null);
setGraphic(null);
if (item!=null && empty==false) {
HBox content = new HBox();
Label firstName = new Label(item.getFirstName()+" ");
firstName.setTextFill(Color.RED);
Label name = new Label(item.getLastName());
name.setTextFill(Color.BLUE);
content.getChildren().addAll(firstName,name);
setGraphic(content);
}
}
}
Pour utiliser ces "cells" custom il faut associer un cell factory à la listview, c'est ce qui est fait en ligne 25 de la classe ListViews :
this.persons.setCellFactory(param -> {
return new PersonCell();
});
Ce qui nous donne le résultat attendu :
Etape 3 - Rendre la listview réactive aux modifications des objets Person
Dans cette section nous allons montrer comment rendre la listview réactive aux modifications de son modèle de données sous-jacent ; c'est à dire comment modifier les éléments courant de la listview.
Récupération du code
git checkout step3
Le code
Pour cela nous allons modifier la classe Person pour lui rajouter la méthode permutFirstNameAndLastName(). Comme son nom l'indique cette méthode permettra de permuter le prénom et le nom d'une personne.
Comment informer la listview des modifications effectuées sur une personne ?
On peut imaginer supprimer de la liste la personne à modifier ; puis la rajouter une fois la modification effectuée. Cela marche, il suffit de faire quelque chose de ce style :
//suppression de la personne p1
persons.getItems().remove(p1);
p1.permutFirstNameAndLastName();
// rajout de p1 modifié
persons.getItems().add(p1);
Mais ce n'est pas la meilleur des solutions ; on peut facilement faire que la listview se modifie automatiquement quand son modèle de données est modifié. Pour cela nous allons utiliser les "propriétés" de javafx. Voir
cet article (en anglais) pour tous les détails.
Modification de la classe Person
La classe Person est modifiée ainsi : les variables de classe firstName et lastName ne sont plus des instances de String mais des instances de StringProperty. En voici le code :
public class Person {
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty();
this.firstName.set(firstName);
this.lastName = new SimpleStringProperty();
this.lastName.set(lastName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getFirstName() {
return firstName.get();
}
public String getLastName() {
return lastName.get();
}
@Override
public String toString() {
return getFirstName()+" "+getLastName();
}
/**
* permut first and last name
*/
public void permutFirstNameAndLastName() {
String first = this.firstName.get();
this.firstName.set(getLastName());
this.lastName.set(first);
}
private StringProperty firstName;
private StringProperty lastName;
}
Modification de la listview
La classe ListViews est modifiée pour rajouter un bouton permuttant le nom/prénom de la personne sélectionnée :
//this button will permute the selected person name and first name
Button permut = new Button("permut");
permut.setOnAction(event -> {
Person selected = this.persons.getSelectionModel().getSelectedItem();
if (selected!=null) {
selected.permutFirstNameAndLastName();
}
});
Enfin, la liste observable à laquelle la liste view sera couplée est construite "à la main" de façon à ce que la listview soit informée des changements sur les instances de Person que l'on veut propager. Le code tient en une seule ligne :
// associate the listview with a custom observable list
this.persons.setItems(FXCollections.observableArrayList(param -> new Observable[]{param.firstNameProperty(),param.lastNameProperty()} ) );
En résumé, pour chaque instance de personne on retourne un tableau d'observable qui contiendra la liste des propriétés à observer.