2016-04-15 22 views
17

Sono un po 'nuovo in Java, JavaFX e programmazione in generale, e ho un problema che mi sta rompendo il cervello.Come è possibile popolare un controllo ListView in JavaFX utilizzando oggetti personalizzati?

Nella maggior parte dei tutorial che hanno guardato su per quanto riguarda la compilazione di un controllo ListView (Utilizzando un ObservableArrayList, più precisamente) il modo più semplice per farlo è quello di rendere da un ObservableList di stringhe, in questo modo:

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

Ma non voglio usare le stringhe. Vorrei utilizzare un oggetto personalizzato che ho fatto chiamato Parole:

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); 

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

Potete vedere dove questo sta continuità aziendale il codice compila e funziona, ma quando lo mostro nella mia domanda, invece di visualizzare i titoli di ogni parola in ListView, visualizza l'oggetto di Word come una stringa!

Image showing my application and its ListView

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

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

In modo tale che, piuttosto che prendere le parole direttamente da wordsList, si accede al wordString proprietà in ogni parola del mio observableArrayList?

Giusto per essere chiari, questo non è per Android, e l'elenco di parole verrà modificato, salvato e caricato alla fine, quindi non posso semplicemente creare un altro array per contenere il 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.

risposta

20

soluzione metodo

vi consiglio di utilizzare un cell factory per risolvere questo problema.

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()); 
     } 
    } 
}); 

Sample 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); 
    } 
} 

Note di attuazione

Anche se si potrebbe ignorare toString nella classe Word per fornire una rappresentazione di stringa del termine volto a rappresentazione nel tuo ListVie Vorrei raccomandare di fornire una fabbrica di celle nel ListView per l'estrazione dei dati della vista dalla parola oggetto e la sua rappresentazione nel vostro ListView. Usando questo approccio si ottiene la separazione delle preoccupazioni in quanto non si lega la vista grafica del proprio oggetto 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 è possibile applicare vari nodi grafici per creare una rappresentazione visiva dei dati oltre una semplice stringa di testo (se si desidera farlo).

Inoltre, come consiglio a parte, raccomando di rendere gli oggetti di Word immutable objects, 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'oggetto. 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 oggetto parola visualizzato, la vista elenco non raccoglierà modifiche al definizione senza logica di listener appropriata nella fabbrica di celle ListView.

+0

Grazie mille per la tua risposta molto dettagliata! Per ora ho intenzione di provare a sovrascrivere il metodo toString(), ma ho intenzione di fare un po 'più di lettura su Fabbrica celle per comprenderli meglio, e quindi modificare l'applicazione per usarli invece come da tuo suggerimento. Modificherò solo la 'definizione' di ogni singola parola con quel pulsante di modifica, e la vista elenco traccia solo la proprietà 'parola', quindi non è necessario tenere traccia delle modifiche alla 'definizione'. –

1

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!

+0

Hmmm, questo ha senso! Darò un colpo; in questo momento ho una lezione da frequentare, ma proverò più tardi e vedrò se funziona. –

+0

Ma non è una soluzione molto buona: si potrebbe desiderare che il metodo 'toString()' restituisca qualcosa di diverso da ciò che si desidera visualizzare nell'interfaccia utente. –

0

Quello che ListView sta visualizzando al momento è toString() di Word. Per risolvere il problema è sufficiente aggiungere il seguente metodo nella classe Word (questo è solo un esempio):

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