Come posso popolare un ListView in JavaFX utilizzando oggetti personalizzati?

Sono un po ‘nuovo a Java, JavaFX e programmazione in generale, e ho un problema che mi sta rompendo il cervello.

Nella maggior parte delle esercitazioni che ho cercato di compilare un ListView (usando una ObservableArrayList, più specificamente) il modo più semplice per farlo è quello di farlo da una ObservableList of Strings, in questo modo:

ObservableList wordsList = FXCollections.observableArrayList("First word","Second word", "Third word", "Etc."); ListView listViewOfStrings = new ListView(wordsList); 

Ma non voglio usare le stringhe. Vorrei utilizzare un object personalizzato che ho creato chiamato Words:

 ObservableList 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 listViewOfWords = new ListView(wordsList); 

Ogni object di Word ha solo 2 proprietà: wordString (una stringa della parola) e definizione (un’altra stringa che è la definizione della parola). Ho getter e setter per entrambi.

Puoi vedere dove sta andando – il codice compila e funziona, ma quando lo visualizzo nella mia applicazione, piuttosto che visualizzare i titoli di ogni parola in ListView, visualizza l’object Word stesso come una stringa!

Immagine che mostra la mia applicazione e il suo ListView

La mia domanda qui è, in particolare, c’è un modo semplice per riscrivere questo:

 ListView listViewOfWords = new ListView(wordsList); 

In questo modo, piuttosto che prendere le parole direttamente da wordsList, accede alla proprietà wordString in ogni parola di my observableArrayList?

Giusto per essere chiari, questo non è per Android, e l’elenco delle parole verrà modificato, salvato e caricato alla fine, quindi non posso semplicemente creare un altro array per contenere le wordString. Ho fatto un po ‘di ricerche sul web e sembra esserci una cosa chiamata “Cell Factories”, ma sembra inutilmente complicata per quello che sembra essere un problema così semplice, e come ho affermato prima, sono un po’ un principiante quando si tratta di programmazione.

Qualcuno può aiutare? Questa è la mia prima volta qui, quindi mi dispiace se non ho incluso abbastanza del mio codice o ho fatto qualcosa di sbagliato.

Approccio alla soluzione

Consiglio di utilizzare una fabbrica di celle per risolvere questo problema.

 listViewOfWords.setCellFactory(param -> new ListCell() { @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()); } } }); 

Applicazione di esempio

Aggiungi immagine

 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 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 listViewOfWords = new ListView<>(wordsList); listViewOfWords.setCellFactory(param -> new ListCell() { @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); } } 

Note di implementazione

Sebbene tu possa eseguire l’override di toString nella tua class di Word per fornire una rappresentazione stringa della parola destinata alla rappresentazione nel tuo ListView, ti consiglio di fornire una cella nel ListView per estrarre i dati della vista dalla parola object e la sua rappresentazione nel tuo Visualizzazione elenco. Usando questo approccio si ottiene la separazione delle preoccupazioni in quanto non si lega la vista grafica del proprio object Word con il suo metodo toString testuale; quindi toString potrebbe continuare ad avere output diversi (ad esempio informazioni complete sui campi di Word con un nome di parola e una descrizione a scopo di debug). Inoltre, una fabbrica di celle è più flessibile in quanto è ansible applicare vari nodes grafici per creare una rappresentazione visiva dei dati oltre una semplice stringa di testo (se si desidera farlo).

Inoltre, per inciso, ti consiglio di rendere oggetti Word immutabili , rimuovendo i loro setter. Se è davvero necessario modificare gli oggetti parola stessi, il modo migliore per gestirli è disporre delle proprietà osservabili esposte per i campi dell’object. Se vuoi anche che la tua interfaccia utente si aggiorni mentre le proprietà osservabili dei tuoi oggetti cambiano, allora devi rendere le tue celle di lista consapevoli delle modifiche agli elementi associati, ascoltando le modifiche a loro (che è un po ‘più complesso in questo Astuccio). Si noti che l’elenco contenente le parole è già osservabile e ListView si occuperà di gestire le modifiche a tale elenco, ma se si è modificata la definizione di parola per esempio all’interno di un object parola visualizzato, la vista elenco non raccoglierà modifiche al definizione senza logica di listener appropriata nella fabbrica di celle ListView.

Scommetto che Nick ListView sta chiamando il metodo toString () su Word. Se non lo hai sovrascritto, utilizza il metodo toString () predefinito, che restituisce solo quelle … informazioni non così utili. Esegui l’override per generare una stringa formattata in modo bello, e dovresti essere pronto!

Ciò che il tuo ListView sta visualizzando al momento è toString () di Word. Per risolvere il tuo problema basta aggiungere il seguente metodo nella tua class di Word (questo è solo un esempio):

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