2016-01-17 9 views
7

Sto tentando di avere una ListView personalizzata fatta di Cell personalizzata in base a un elenco personalizzato objects.Fabbrica di celle personalizzate JavaFX con oggetti personalizzati

L'oggetto personalizzato è il nome classe chiamata Message che contiene un paio di campi per il messaggio contenuti, destinatario, timestamp e stato (leggere, inviata ecc).

Dopo aver guardato a questa domanda: Customize ListView in JavaFX with FXML ho successo:

  1. creato un ListView con cellule personalizzati dove il disegno cella è definita in un file di FXML;
  2. associato a un controller in modo che i dati di ciascuna cella possano essere riempiti con l'elemento corrente della raccolta;

Tuttavia, non sono riuscito a collegare entrambi: non riesco a trovare un modo in modo che la voce corrente del ListView viene inviato il controller cella.

Ecco il mio codice per la fabbrica delle cellule e il riempimento ListView di elementi:

final ObservableList observableList = FXCollections.observableArrayList(); 
observableList.setAll(myMessages); //assume myMessage is a ArrayList<Message> 
conversation.setItems(observableList); //the listview 
conversation.setCellFactory(new Callback<ListView<Message>, ListCell<Message>>() { 
    @Override 
    public ConversationCell<Message> call(ListView<Message> listView) { 
     return new ConversationCell(); 
    } 
}); 

E ora, la classe ConversationCell:

public final class ConversationCell<Message> extends ListCell<Message> { 

    @Override 
    protected void updateItem(Message item, boolean empty) { 
     super.updateItem(item, empty); 
     ConversationCellController ccc = new ConversationCellController(null); 
     setGraphic(ccc.getView()); 
    } 
} 

non posso mostrare il ConversationCellController ma tutto quello che posso dire , questo è dove (nel suo costruttore) carico il file FXML che progetta la cella e quindi posso riempire i valori con l'oggetto Messaggio specificato.

Il metodo getView() restituisce il riquadro radice che contiene la cella ora piena e progettata.

Come ho già detto, il lavoro di progettazione, ma io non riesco a collegare gli elementi ListView con il CellFactory perché nel metodo

protetta updateItem vuoto (oggetto del messaggio, booleano vuoto)

vuoto è impostato su true e l'articolo è effettivamente null.

Cosa posso fare per fare in modo che funzioni?

+1

'vuoto' è impostato su true su tutte le istanze? una cella è considerata vuota se non ha alcun oggetto, cioè è solo lì per riempire lo spazio sotto le celle effettive. Se il tuo ListView è abbastanza grande da mostrare 10 elementi ma ne hai solo 3, avrai 7 oggetti 'ConversationCell' che sono vuoti. Per inciso, non riesco a vedere dove 'ConversationCellController' dovrebbe ottenere l'oggetto. Se si è certi che si ottenga 'empty = true' su celle con dati reali, potrebbe essere utile se si riuscisse a trovare un esempio minimo che mostri questo problema. – Itai

+2

Penso che l'implementazione di 'ListView' crea inizialmente abbastanza celle per riempire il display e chiama' updateItem (null, true); 'su tutte loro, quindi chiama' updateItem (...) 'su quelle non vuote, con i dati appropriati (anche se non ne sono certo, e non ho il tempo di scavare il codice sorgente in questo momento). In ogni caso, tutte le implementazioni di celle devono gestire il caso di cella vuota. –

+0

"Non riesco a mostrare il ConversationCellController" Perché no? –

risposta

5

Tutte le implementazioni di celle personalizzate che eseguono l'override di updateItem(...) devono occuparsi del caso in cui la cella è vuota in tale metodo. Così si potrebbe fare una correzione ingenuo di questo con

public final class ConversationCell<Message> extends ListCell<Message> { 

    @Override 
    protected void updateItem(Message item, boolean empty) { 
     super.updateItem(item, empty); 
     if (empty) { 
      setGraphic(null); 
     } else { 
      // did you mean to pass null here, or item?? 
      ConversationCellController ccc = new ConversationCellController(null); 
      setGraphic(ccc.getView()); 
     } 
    } 
} 

Tuttavia, questo non è una buona soluzione dal punto di vista delle prestazioni.Stai caricando l'FXML ogni volta che updateItem(...) viene chiamato con una cella non vuota, ed è un'operazione piuttosto costosa (potenzialmente coinvolgente i/o del file, decompressione del file FXML da un file jar, analisi del file, molta riflessione, creazione di nuovi Elementi dell'interfaccia utente, ecc.). Non vuoi chiedere al Thread di applicazione FX di fare tutto quel lavoro ogni volta che l'utente fa scorrere la vista elenco di alcuni pixel. Invece, il cellulare dovrebbe memorizzare nella cache il nodo e dovrebbe aggiornarlo nel metodo updateItem:

public final class ConversationCell<Message> extends ListCell<Message> { 

    private final ConversationCellController ccc = new ConversationCellController(null); 
    private final Node view = ccc.getView(); 

    @Override 
    protected void updateItem(Message item, boolean empty) { 
     super.updateItem(item, empty); 
     if (empty) { 
      setGraphic(null); 
     } else { 
      ccc.setItem(item); 
      setGraphic(view); 
     } 
    } 
} 

è necessario definire un metodo setItem(...) nel ConversationCellController che aggiorna la vista (testo set su etichette, etc etc) di conseguenza.

+0

Ehi, questo è davvero un ottimo punto! Proprio come l'adattatore ListView di Android con il pattern ViewHolder per tenere traccia delle viste già generate per ridurre l'utilizzo e il ritardo della CPU. Proverò la tua idea al più presto. Potresti anche dirmi cosa posso fare per attivare gli eventi, con il mio codice di installazione/codice corrente, in modo che una cella specifica venga aggiornata? Ho una barra di avanzamento per un messaggio inviato e vorrei aggiornare i suoi progressi, per esempio. Grazie ! – Mackovich

+0

Per gli aggiornamenti, che è una domanda diversa, è possibile disporre che la visualizzazione elenco aggiorni le sue celle utilizzando le proprietà JavaFX nella classe 'Message' e creando l'elenco di elementi con un estrattore. Vedi http://stackoverflow.com/questions/23822550/ –

+0

grazie. Darò un'occhiata ! Tra l'altro ho implementato le tue soluzioni e funziona perfettamente. Non avevo molti elementi (solo alcuni) nella mia ObservableList ma mi sembra più nitido! Ad ogni modo, ho anche notato che 'ccc.setItem' è chiamato più volte quindi ho dovuto aggiungere un valore booleano in' ConversationCellController' per evitare di impostare le viste a molte ore. Anche io avevo ragione? Per ora funziona, ma mi chiedo ... grazie! – Mackovich