web-dev-qa-db-fra.com

Comment puis-je remplir un ListView dans JavaFX à l'aide d'objets personnalisés?

Je suis un peu nouveau sur Java, JavaFX et la programmation en général, et j'ai un problème qui me brise le cerveau.

Dans la plupart des didacticiels que j'ai consultés concernant le remplissage d'un ListView (en utilisant un ObservableArrayList, plus spécifiquement), la façon la plus simple de le faire est de le faire à partir d'une ObservableList de chaînes, comme ceci:

ObservableList<String> wordsList = FXCollections.observableArrayList("First Word","Second Word", "Third Word", "Etc."); 
ListView<String> listViewOfStrings = new ListView<>(wordsList);

Mais je ne veux pas utiliser de chaînes. Je voudrais utiliser un objet personnalisé que j'ai créé, appelé Words:

ObservableList<Word> wordsList = FXCollections.observableArrayList();
wordsList.add(new Word("First Word", "Definition of First Word");
wordsList.add(new Word("Second Word", "Definition of Second Word");
wordsList.add(new Word("Third Word", "Definition of Third Word");
ListView<Word> listViewOfWords = new ListView<>(wordsList);

Chaque objet Word n'a que 2 propriétés: wordString (une chaîne du mot) et definition (une autre chaîne qui est la définition du mot). J'ai des getters et des setters pour les deux.

Vous pouvez voir où cela va - le code se compile et fonctionne, mais lorsque je l'affiche dans mon application, plutôt que d'afficher les titres de chaque mot dans ListView, il affiche l'objet Word lui-même sous forme de chaîne!

Image montrant mon application et son ListView

Ma question ici est, en particulier, existe-t-il un moyen simple de réécrire ceci:

ListView<Word> listViewOfWords = new ListView<>(wordsList);

De telle manière que, plutôt que de prendre des mots directement de wordsList, il accède à la propriété wordString dans chaque mot de mon observableArrayList?

Juste pour être clair, ce n'est pas pour Android, et la liste des mots sera modifiée, enregistrée et chargée éventuellement, donc je ne peux pas simplement créer un autre tableau pour contenir les chaînes de mots. J'ai fait un peu de recherche sur le Web et il semble y avoir une chose appelée "Cell Factories", mais cela semble inutilement compliqué pour ce qui semble être un problème aussi simple, et comme je l'ai déjà dit, je suis un peu un débutant en matière de programmation.

Quelqu'un peut-il aider? C'est ma première fois ici, donc je suis désolé si je n'ai pas inclus suffisamment de mon code ou si j'ai fait quelque chose de mal.

23
David Collins

Approche de la solution

Je conseille d'utiliser un cell factory pour résoudre ce problème.

listViewOfWords.setCellFactory(param -> new ListCell<Word>() {
    @Override
    protected void updateItem(Word item, boolean empty) {
        super.updateItem(item, empty);

        if (empty || item == null || item.getWord() == null) {
            setText(null);
        } else {
            setText(item.getWord());
        }
    }
});

Exemple d'application

add image

import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class CellFactories extends Application {    
    @Override
    public void start(Stage stage) {
        ObservableList<Word> wordsList = FXCollections.observableArrayList();
        wordsList.add(new Word("First Word", "Definition of First Word"));
        wordsList.add(new Word("Second Word", "Definition of Second Word"));
        wordsList.add(new Word("Third Word", "Definition of Third Word"));
        ListView<Word> listViewOfWords = new ListView<>(wordsList);
        listViewOfWords.setCellFactory(param -> new ListCell<Word>() {
            @Override
            protected void updateItem(Word item, boolean empty) {
                super.updateItem(item, empty);

                if (empty || item == null || item.getWord() == null) {
                    setText(null);
                } else {
                    setText(item.getWord());
                }
            }
        });
        stage.setScene(new Scene(listViewOfWords));
        stage.show();
    }

    public static class Word {
        private final String Word;
        private final String definition;

        public Word(String Word, String definition) {
            this.Word = Word;
            this.definition = definition;
        }

        public String getWord() {
            return Word;
        }

        public String getDefinition() {
            return definition;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Notes de mise en œuvre

Bien que vous puissiez remplacer toString dans votre classe Word pour fournir une représentation sous forme de chaîne du mot destiné à la représentation dans votre ListView, je recommanderais de fournir une fabrique de cellules dans le ListView pour l'extraction des données de vue de l'objet Word et sa représentation dans votre ListView. En utilisant cette approche, vous obtenez une séparation des préoccupations car vous ne liez pas la vue graphique de votre objet Word avec sa méthode textuelle toString; toString peut donc continuer à avoir des sorties différentes (par exemple des informations complètes sur les champs Word avec un nom Word et une description à des fins de débogage). De plus, une fabrique de cellules est plus flexible car vous pouvez appliquer divers nœuds graphiques pour créer une représentation visuelle de vos données au-delà d'une simple chaîne de texte (si vous souhaitez le faire).

Aussi, en passant, je recommande de faire vos objets Word objets immuables , en supprimant leurs setters. Si vous avez vraiment besoin de modifier les objets Word eux-mêmes, la meilleure façon de gérer cela est d'avoir des propriétés observables exposées pour les champs d'objet. Si vous souhaitez également que votre interface utilisateur soit mise à jour à mesure que les propriétés observables de vos objets changent, vous devez informer vos cellules de liste des modifications apportées aux éléments associés, en écoutant les modifications qui leur sont apportées (ce qui est un peu plus complexe dans ce cas). Cas). Notez que la liste contenant les mots est déjà observable et ListView se chargera de gérer les modifications apportées à cette liste, mais si vous avez modifié la définition de Word par exemple dans un objet Word affiché, votre affichage de liste ne reprendra pas les modifications apportées à la définition sans logique d'écouteur appropriée dans la fabrique de cellules ListView.

31
jewelsea

Ce que votre ListView affiche en ce moment, c'est la chaîne toString () de Word. Pour résoudre votre problème, ajoutez simplement la méthode suivante dans votre classe Word (ce n'est qu'un exemple):

@Override
public String toString(){
    return (this.Word + " --- Definition: " + this.definition);
}
2

Je vous parie un nickel que ListView appelle la méthode toString () sur Word. Si vous ne l'avez pas remplacé, il utilise la méthode toString () par défaut, qui ne produit que ces informations ... pas si utiles. Remplacez-le pour produire une chaîne joliment formatée, et vous devriez être prêt à partir!

2
GaiusOctavian