↑
CHAPITRE 6.2

ListView

Maîtriser l'affichage de listes et la sélection dans JavaFX
Dans cette section, vous allez apprendre à utiliser le composant ListView de JavaFX pour afficher des listes d'éléments. Vous découvrirez comment afficher des données, gérer la sélection, lier une ListView à un modèle de données, et personnaliser l'affichage avec une CellFactory. Le ListView est l'un des composants les plus utilisés pour afficher des collections de données.

6.2ListView

6.2.1 – Affichage et sélection

Le ListView est un composant JavaFX qui affiche une liste d'éléments dans une zone défilable. Il permet à l'utilisateur de sélectionner un ou plusieurs éléments et d'interagir avec la liste. C'est le composant idéal pour afficher des collections de données.

Exemple ListView - Affichage et sélection
Exemple d'application ListView avec affichage et gestion de la sélection

🎯 Caractéristiques du ListView

Le ListView offre plusieurs fonctionnalités :

  • Affichage vertical : Les Ă©lĂ©ments sont affichĂ©s en colonne, un par ligne
  • DĂ©filement automatique : Si la liste est longue, une barre de dĂ©filement apparaĂ®t automatiquement
  • SĂ©lection simple ou multiple : L'utilisateur peut sĂ©lectionner un ou plusieurs Ă©lĂ©ments
  • Liaison avec ObservableList : Se met Ă  jour automatiquement quand la liste change
  • Personnalisation : Vous pouvez personnaliser l'affichage avec une CellFactory

📝 Créer une ListView simple

Pour créer une ListView, vous devez d'abord créer une ObservableList et la lier à la ListView :

Exemple 1 : ListView basique avec String

package application;

import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private ListView<String> listViewItems;
    
    private ObservableList<String> items;
    
    @FXML
    private void initialize() {
        // Créer l'ObservableList avec des éléments
        items = FXCollections.observableArrayList(
            "Pomme", "Banane", "Orange", "Fraise", "Cerise"
        );
        
        // Lier la ListView Ă  l'ObservableList
        listViewItems.setItems(items);
        
        // Maintenant, la ListView affiche les 5 éléments
    }
}
Explication :
• ListView<String> : Crée une ListView qui affiche des String
• setItems(items) : Lie l'ObservableList à la ListView
• Les éléments sont affichés automatiquement dans la ListView

📝 Gérer la sélection

Le ListView permet de gérer la sélection d'éléments. Vous pouvez récupérer l'élément sélectionné ou écouter les changements de sélection :

Exemple 2 : Récupérer l'élément sélectionné

@FXML
private void afficherSelection() {
    // Récupérer l'élément sélectionné
    String itemSelectionne = listViewItems.getSelectionModel().getSelectedItem();
    
    if (itemSelectionne != null) {
        System.out.println("Élément sélectionné : " + itemSelectionne);
    } else {
        System.out.println("Aucun élément sélectionné");
    }
}

@FXML
private void afficherIndex() {
    // Récupérer l'index de l'élément sélectionné
    int indexSelectionne = listViewItems.getSelectionModel().getSelectedIndex();
    
    if (indexSelectionne >= 0) {
        System.out.println("Index sélectionné : " + indexSelectionne);
    } else {
        System.out.println("Aucun élément sélectionné");
    }
}

Exemple 3 : Écouter les changements de sélection

@FXML
private void initialize() {
    items = FXCollections.observableArrayList("Pomme", "Banane", "Orange");
    listViewItems.setItems(items);
    
    // Écouter les changements de sélection
    listViewItems.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
        if (newVal != null) {
            System.out.println("Nouvelle sélection : " + newVal);
            System.out.println("Ancienne sélection : " + oldVal);
        } else {
            System.out.println("Sélection supprimée");
        }
    });
}

📝 Sélection multiple

Par défaut, la ListView permet la sélection d'un seul élément. Pour activer la sélection multiple :

@FXML
private void initialize() {
    items = FXCollections.observableArrayList("Pomme", "Banane", "Orange", "Fraise");
    listViewItems.setItems(items);
    
    // Activer la sélection multiple
    listViewItems.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    
    // Maintenant, l'utilisateur peut sélectionner plusieurs éléments
    // en maintenant Ctrl (ou Cmd sur Mac) et en cliquant
}

@FXML
private void afficherSelectionsMultiples() {
    // Récupérer tous les éléments sélectionnés
    ObservableList<String> selections = listViewItems.getSelectionModel().getSelectedItems();
    
    System.out.println("Éléments sélectionnés : " + selections);
    System.out.println("Nombre : " + selections.size());
}
Modes de sélection :
• SelectionMode.SINGLE : Sélection d'un seul élément (par défaut)
• SelectionMode.MULTIPLE : Sélection de plusieurs éléments (Ctrl/Cmd + clic)

📝 Méthodes utiles du SelectionModel

Le SelectionModel offre plusieurs méthodes pour gérer la sélection :

SelectionModel<String> selectionModel = listViewItems.getSelectionModel();

// Récupérer la sélection
String selected = selectionModel.getSelectedItem();        // Élément sélectionné
int index = selectionModel.getSelectedIndex();             // Index sélectionné
ObservableList<String> selectedItems = selectionModel.getSelectedItems(); // Tous les éléments sélectionnés

// Modifier la sélection
selectionModel.select(0);              // Sélectionner le premier élément
selectionModel.select("Pomme");        // Sélectionner par valeur
selectionModel.clearSelection();       // Désélectionner tout
selectionModel.selectNext();           // Sélectionner l'élément suivant
selectionModel.selectPrevious();       // Sélectionner l'élément précédent

// Vérifier la sélection
boolean hasSelection = selectionModel.isEmpty();            // Vérifier si rien n'est sélectionné
int count = selectionModel.getSelectedIndices().size();    // Nombre d'éléments sélectionnés

📝 Exemple complet : ListView avec sélection

Créons un exemple complet qui affiche une liste et permet de gérer la sélection :

MainController.java
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.SelectionMode;

public class MainController {
    
    @FXML
    private ListView<String> listViewItems;
    
    @FXML
    private TextField txtNouvelItem;
    
    @FXML
    private Button btnAjouter;
    
    @FXML
    private Button btnSupprimer;
    
    @FXML
    private Label lblSelection;
    
    private ObservableList<String> items;
    
    @FXML
    private void initialize() {
        // Créer l'ObservableList
        items = FXCollections.observableArrayList(
            "Pomme", "Banane", "Orange", "Fraise", "Cerise"
        );
        
        // Lier Ă  la ListView
        listViewItems.setItems(items);
        
        // Activer la sélection multiple (optionnel)
        listViewItems.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        
        // Écouter les changements de sélection
        listViewItems.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
            if (newVal != null) {
                lblSelection.setText("Sélectionné : " + newVal);
            } else {
                lblSelection.setText("Aucune sélection");
            }
        });
        
        // Initialiser le label
        lblSelection.setText("Aucune sélection");
    }
    
    @FXML
    private void ajouterItem() {
        String nouvelItem = txtNouvelItem.getText().trim();
        if (!nouvelItem.isEmpty()) {
            items.add(nouvelItem);
            txtNouvelItem.clear();
        }
    }
    
    @FXML
    private void supprimerItem() {
        String selectionne = listViewItems.getSelectionModel().getSelectedItem();
        if (selectionne != null) {
            items.remove(selectionne);
        }
    }
    
    @FXML
    private void afficherSelection() {
        ObservableList<String> selections = listViewItems.getSelectionModel().getSelectedItems();
        if (!selections.isEmpty()) {
            System.out.println("Éléments sélectionnés : " + selections);
        }
    }
}
Points importants :
• La ListView est liée à une ObservableList avec setItems()
• Le SelectionModel permet de gérer la sélection
• Un listener sur selectedItemProperty() détecte les changements
• La sélection multiple est activée avec setSelectionMode()

💡 Points clés à retenir

  • ListView : Composant pour afficher une liste d'Ă©lĂ©ments
  • setItems() : Lie une ObservableList Ă  la ListView
  • getSelectionModel() : Accède au modèle de sĂ©lection
  • getSelectedItem() : RĂ©cupère l'Ă©lĂ©ment sĂ©lectionnĂ© (sĂ©lection simple)
  • getSelectedItems() : RĂ©cupère tous les Ă©lĂ©ments sĂ©lectionnĂ©s (sĂ©lection multiple)
  • selectedItemProperty() : PropriĂ©tĂ© observable pour Ă©couter les changements
  • SelectionMode : SINGLE (par dĂ©faut) ou MULTIPLE
Conseil : Utilisez toujours ObservableList avec ListView pour bénéficier de la mise à jour automatique. Testez la sélection simple et multiple pour bien comprendre les différences !

6.2.2 – Liaison à un modèle simple

Dans cette section, vous allez apprendre à lier une ListView à un modèle de données simple. Au lieu d'afficher des String, vous afficherez des objets personnalisés. JavaFX utilise la méthode toString() de vos objets pour les afficher dans la ListView.

🎯 Pourquoi utiliser un modèle ?

Utiliser un modèle (classe personnalisée) au lieu de String permet de :

  • Stockage de donnĂ©es complexes : Chaque Ă©lĂ©ment peut contenir plusieurs propriĂ©tĂ©s
  • Organisation : Les donnĂ©es sont structurĂ©es et typĂ©es
  • ExtensibilitĂ© : Facile d'ajouter de nouvelles propriĂ©tĂ©s
  • RĂ©utilisabilitĂ© : Le mĂŞme modèle peut ĂŞtre utilisĂ© dans plusieurs composants

📝 Exemple : Classe Personne

Créons une classe Personne simple pour illustrer :

Personne.java

package application;

public class Personne {
    private String nom;
    private String prenom;
    private int age;
    
    public Personne(String nom, String prenom, int age) {
        this.nom = nom;
        this.prenom = prenom;
        this.age = age;
    }
    
    // Getters
    public String getNom() { return nom; }
    public String getPrenom() { return prenom; }
    public int getAge() { return age; }
    
    // Setters
    public void setNom(String nom) { this.nom = nom; }
    public void setPrenom(String prenom) { this.prenom = prenom; }
    public void setAge(int age) { this.age = age; }
    
    // Méthode toString() - Utilisée par la ListView pour afficher l'objet
    @Override
    public String toString() {
        return prenom + " " + nom + " (" + age + " ans)";
    }
}
Important :
• La méthode toString() est utilisée par la ListView pour afficher chaque élément
• Si vous ne redéfinissez pas toString(), la ListView affichera quelque chose comme "Personne@123abc"
• Redéfinissez toujours toString() pour avoir un affichage lisible

📝 Utiliser Personne dans une ListView

Exemple ListView - Liaison à un modèle simple
Exemple d'application ListView avec liaison à un modèle personnalisé (Personne)

MainController.java

package application;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private ListView<Personne> listViewPersonnes;
    
    @FXML
    private Label lblDetails;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer l'ObservableList de Personne
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25),
            new Personne("Martin", "Marie", 30),
            new Personne("Bernard", "Pierre", 28),
            new Personne("Dubois", "Sophie", 32)
        );
        
        // Lier Ă  la ListView
        listViewPersonnes.setItems(personnes);
        
        // Écouter les changements de sélection
        listViewPersonnes.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
            if (newVal != null) {
                // Afficher les détails de la personne sélectionnée
                String details = String.format("Nom : %s\nPrénom : %s\nÂge : %d ans",
                    newVal.getNom(), newVal.getPrenom(), newVal.getAge());
                lblDetails.setText(details);
            } else {
                lblDetails.setText("Aucune personne sélectionnée");
            }
        });
    }
    
    @FXML
    private void ajouterPersonne() {
        Personne nouvelle = new Personne("Nouveau", "Nom", 20);
        personnes.add(nouvelle);
        // La ListView se met Ă  jour automatiquement !
    }
    
    @FXML
    private void supprimerPersonne() {
        Personne selectionnee = listViewPersonnes.getSelectionModel().getSelectedItem();
        if (selectionnee != null) {
            personnes.remove(selectionnee);
        }
    }
}
Explication :
• ListView<Personne> : ListView qui affiche des objets Personne
• ObservableList<Personne> : Liste observable de Personne
• La ListView appelle toString() pour afficher chaque Personne
• Quand vous sélectionnez une Personne, vous pouvez accéder à toutes ses propriétés

📝 Récupérer les données de l'élément sélectionné

Une fois qu'un élément est sélectionné, vous pouvez accéder à toutes ses propriétés :

@FXML
private void afficherDetails() {
    Personne selectionnee = listViewPersonnes.getSelectionModel().getSelectedItem();
    
    if (selectionnee != null) {
        // Accéder aux propriétés de l'objet
        String nom = selectionnee.getNom();
        String prenom = selectionnee.getPrenom();
        int age = selectionnee.getAge();
        
        System.out.println("Nom complet : " + prenom + " " + nom);
        System.out.println("Âge : " + age + " ans");
    }
}

@FXML
private void modifierPersonne() {
    Personne selectionnee = listViewPersonnes.getSelectionModel().getSelectedItem();
    
    if (selectionnee != null) {
        // Modifier les propriétés
        selectionnee.setNom("Nouveau nom");
        selectionnee.setAge(30);
        
        // Pour que la ListView se mette Ă  jour, il faut appeler refresh()
        // (sauf si vous utilisez un extractor - voir section 6.1.3)
        listViewPersonnes.refresh();
    }
}
Important :
• Si vous modifiez les propriétés d'un objet dans la liste, la ListView ne se met pas à jour automatiquement
• Vous devez appeler listView.refresh() pour forcer la mise à jour
• Ou utilisez un extractor avec ObservableList (voir section 6.1.3) pour une mise à jour automatique

💡 Points clés à retenir

  • Modèle personnalisĂ© : CrĂ©ez une classe pour reprĂ©senter vos donnĂ©es
  • toString() : RedĂ©finissez cette mĂ©thode pour l'affichage dans la ListView
  • ObservableList<Type> : Utilisez le type de votre modèle
  • getSelectedItem() : Retourne l'objet complet, pas juste une String
  • refresh() : NĂ©cessaire si vous modifiez les propriĂ©tĂ©s d'un objet
  • Extractor : Pour une mise Ă  jour automatique (voir section 6.1.3)
Conseil : Commencez avec un modèle simple (comme Personne). Une fois que vous maîtrisez, vous pourrez créer des modèles plus complexes avec plusieurs propriétés et relations.

6.2.3 – CellFactory simple (optionnel)

Par défaut, la ListView affiche chaque élément en appelant sa méthode toString(). Si vous voulez personnaliser l'affichage (couleurs, icônes, mise en forme, etc.), vous pouvez utiliser une CellFactory. Cette section présente une approche simple pour personnaliser l'affichage.

Note : La CellFactory est optionnelle. Dans la plupart des cas, toString() est suffisant. Utilisez une CellFactory seulement si vous avez besoin de personnaliser l'affichage.

🎯 Quand utiliser une CellFactory ?

Utilisez une CellFactory quand vous voulez :

  • Personnaliser l'apparence : Couleurs, polices, styles diffĂ©rents selon les Ă©lĂ©ments
  • Ajouter des icĂ´nes : Afficher des images ou des icĂ´nes Ă  cĂ´tĂ© du texte
  • Mise en forme avancĂ©e : Disposition complexe avec plusieurs Ă©lĂ©ments
  • Affichage conditionnel : Afficher diffĂ©remment selon certaines conditions

📝 Exemple 1 : CellFactory simple avec Label

Exemple ListView - CellFactory simple
Exemple d'application ListView avec CellFactory pour personnaliser l'affichage

Créons une CellFactory simple qui personnalise l'affichage avec des couleurs :

MainController.java

package application;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.util.Callback;

public class MainController {
    
    @FXML
    private ListView<String> listViewItems;
    
    private ObservableList<String> items;
    
    @FXML
    private void initialize() {
        items = FXCollections.observableArrayList(
            "Pomme", "Banane", "Orange", "Fraise", "Cerise"
        );
        
        listViewItems.setItems(items);
        
        // Définir une CellFactory pour personnaliser l'affichage
        listViewItems.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
            @Override
            public ListCell<String> call(ListView<String> param) {
                return new ListCell<String>() {
                    @Override
                    protected void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);
                        
                        if (empty || item == null) {
                            setText(null);
                            setGraphic(null);
                        } else {
                            // Créer un Label personnalisé
                            Label label = new Label(item);
                            
                            // Appliquer des styles selon l'élément
                            if (item.startsWith("P")) {
                                label.setStyle("-fx-text-fill: red; -fx-font-weight: bold;");
                            } else if (item.startsWith("B")) {
                                label.setStyle("-fx-text-fill: yellow; -fx-font-weight: bold;");
                            } else {
                                label.setStyle("-fx-text-fill: blue;");
                            }
                            
                            setGraphic(label);
                        }
                    }
                };
            }
        });
    }
}

Version lambda (plus simple)

@FXML
private void initialize() {
    items = FXCollections.observableArrayList("Pomme", "Banane", "Orange");
    listViewItems.setItems(items);
    
    // Version lambda (plus simple)
    listViewItems.setCellFactory(listView -> new ListCell<String>() {
        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            
            if (empty || item == null) {
                setText(null);
            } else {
                setText(item);
                // Appliquer un style
                if (item.length() > 5) {
                    setStyle("-fx-text-fill: blue; -fx-font-weight: bold;");
                } else {
                    setStyle("-fx-text-fill: black;");
                }
            }
        }
    });
}
Explication :
• setCellFactory() : Définit comment chaque cellule est créée
• updateItem() : Méthode appelée pour mettre à jour chaque cellule
• empty : Indique si la cellule est vide (pour les cellules de fin de liste)
• setText() ou setGraphic() : Définit le contenu de la cellule

📝 Exemple 2 : CellFactory avec objets personnalisés

Créons une CellFactory pour afficher des objets Personne avec un style personnalisé :

package application;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private ListView<Personne> listViewPersonnes;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25),
            new Personne("Martin", "Marie", 30),
            new Personne("Bernard", "Pierre", 28)
        );
        
        listViewPersonnes.setItems(personnes);
        
        // CellFactory pour Personne
        listViewPersonnes.setCellFactory(listView -> new ListCell<Personne>() {
            @Override
            protected void updateItem(Personne personne, boolean empty) {
                super.updateItem(personne, empty);
                
                if (empty || personne == null) {
                    setText(null);
                    setGraphic(null);
                } else {
                    // Créer un HBox avec plusieurs Labels
                    HBox hbox = new HBox(10);
                    
                    Label lblNom = new Label(personne.getPrenom() + " " + personne.getNom());
                    lblNom.setStyle("-fx-font-weight: bold;");
                    
                    Label lblAge = new Label("(" + personne.getAge() + " ans)");
                    lblAge.setStyle("-fx-text-fill: gray;");
                    
                    // Style selon l'âge
                    if (personne.getAge() < 25) {
                        hbox.setStyle("-fx-background-color: #e3f2fd;");
                    } else if (personne.getAge() < 30) {
                        hbox.setStyle("-fx-background-color: #f3e5f5;");
                    } else {
                        hbox.setStyle("-fx-background-color: #fff3e0;");
                    }
                    
                    hbox.getChildren().addAll(lblNom, lblAge);
                    setGraphic(hbox);
                }
            }
        });
    }
}
Points importants :
• updateItem() est appelée pour chaque cellule
• Toujours vérifier empty et null
• Utilisez setGraphic() pour un contenu personnalisé (HBox, VBox, etc.)
• Utilisez setText() pour un simple texte
• Vous pouvez appliquer des styles conditionnels selon les propriétés de l'objet

📝 Exemple 3 : CellFactory avec icônes

Vous pouvez aussi ajouter des icĂ´nes ou des images dans les cellules :

import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

listViewItems.setCellFactory(listView -> new ListCell<String>() {
    @Override
    protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        
        if (empty || item == null) {
            setText(null);
            setGraphic(null);
        } else {
            // Créer un HBox avec icône et texte
            HBox hbox = new HBox(10);
            
            // Ajouter une icĂ´ne (exemple)
            ImageView icon = new ImageView(new Image("file:icon.png"));
            icon.setFitWidth(16);
            icon.setFitHeight(16);
            
            Label label = new Label(item);
            
            hbox.getChildren().addAll(icon, label);
            setGraphic(hbox);
        }
    }
});

💡 Points clés à retenir

  • CellFactory : Permet de personnaliser l'affichage de chaque cellule
  • setCellFactory() : DĂ©finit la CellFactory pour la ListView
  • updateItem() : MĂ©thode appelĂ©e pour mettre Ă  jour chaque cellule
  • empty : VĂ©rifiez toujours si la cellule est vide
  • setText() : Pour un affichage texte simple
  • setGraphic() : Pour un affichage personnalisĂ© (HBox, VBox, etc.)
  • Optionnel : Utilisez seulement si vous avez besoin de personnalisation
Important :
  • âś… Toujours appeler super.updateItem() en premier
  • âś… Toujours vĂ©rifier empty et null
  • âś… RĂ©initialiser avec setText(null) et setGraphic(null) si vide
  • âś… La CellFactory est optionnelle - utilisez toString() si c'est suffisant
Conseil : Commencez sans CellFactory. Ajoutez-en une seulement si vous avez vraiment besoin de personnaliser l'affichage. Dans la plupart des cas, toString() est suffisant !