2016-01-17 4 views
7

Ich versuche, eine benutzerdefinierte ListView von benutzerdefinierten Cell basierend auf einer Liste von benutzerdefinierten objects.JavaFX benutzerdefinierte Zelle Factory mit benutzerdefinierten Objekten

Das benutzerdefinierte Objekt ist Klassenname Message genannt, die ein paar Felder für die Nachricht enthält Inhalt, Empfänger, Zeitstempel und Status (lesen, gesendet etc.).

Nach auf dieser Frage suchen: Customize ListView in JavaFX with FXML Ich habe erfolgreich:

  1. einen Listview mit individuellen Zellen geschaffen, in dem das Zellen-Design in einer FXML Datei definiert ist;
  2. zugeordnet Controller, so dass jede Zelle Daten mit dem aktuellen Element der Sammlung gefüllt werden kann;

aber ich nicht beides verbinden: Ich kann nicht einen Weg zu finden scheinen, so dass das aktuelle Element des Listview die Controller-Zelle gesendet wird.

Hier ist mein Code für die Zellfabrik und die Listview-Füllung der Objekte:

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

Und nun die ConversationCell Klasse:

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

ich nicht die ConversationCellController zeigen können, aber alles, was ich sagen kann, , das ist, wo (in seinem Konstruktor) ich die FXML-Datei laden, die die Zelle entwirft, und dann kann ich die Werte mit dem gegebenen Nachrichtenelement füllen.

Die getView()-Methode gibt den Root-Bereich zurück, der die jetzt ausgefüllte und entworfene Zelle enthält.

Wie ich bereits sagen, die Gestaltung Arbeit, aber ich kann nicht scheinen, um die Listview-Elemente mit dem CellFactory weil in Verfahren

protected void updateItem (Message Artikel, boolean leer)

verlinken

leer ist auf gesetzt wahr und Artikel ist in der Tat null.

Was kann ich tun, damit dies funktioniert?

+1

Wird 'empty' in allen Instanzen auf true gesetzt? Eine Zelle wird als leer betrachtet, wenn sie kein Objekt hat, dh sie ist nur dazu da, um den Raum unter den eigentlichen Zellen zu füllen. Wenn Ihr ListView groß genug ist, um 10 Objekte anzuzeigen, Sie aber nur 3 Objekte haben, haben Sie 7 'ConversationCell'-Objekte, die leer sind. Nebenbei kann ich nicht sehen, woher 'ConversationCellController' das Element erhalten soll. Wenn Sie immer noch sicher sind, dass Sie auf Zellen mit aktuellen Daten 'empty = true' erhalten, kann es hilfreich sein, ein minimales Beispiel mit diesem Problem zu finden. – Itai

+2

Ich denke, die 'ListView'-Implementierung erstellt zunächst genug Zellen, um die Anzeige zu füllen, und ruft 'updateItem (null, true);' auf allen auf, dann ruft 'updateItem (...)' auf den nicht leeren, mit auf die entsprechenden Daten (obwohl ich nicht sicher bin und keine Zeit habe, den Quellcode zu durchsuchen). Auf jeden Fall müssen alle Zellenimplementierungen den Leerzellenfall handhaben. –

+0

"Ich kann den ConversationCellController nicht anzeigen" Warum nicht? –

Antwort

5

Alle benutzerdefinierten Zellenimplementierungen, die updateItem(...) überschreiben, müssen sich mit dem Fall befassen, in dem die Zelle in dieser Methode leer ist. So können Sie jedoch ein naives fix dies mit

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

tun könnten, dann ist dies aus der Sicht der Leistung keine gute Lösung.Sie laden die FXML jedes Mal, wenn updateItem(...) mit einer nicht leeren Zelle aufgerufen wird, und das ist eine ziemlich teure Operation (möglicherweise mit Datei-E/A, Entpacken der FXML-Datei aus einer JAR-Datei, Parsing der Datei, viel Reflexion, Erstellen neuer UI-Elemente usw.). Sie möchten den FX-Anwendungs-Thread nicht dazu auffordern, all diese Arbeit jedes Mal auszuführen, wenn der Benutzer die Listenansicht um einige Pixel scrollt. Stattdessen sollten Sie Ihre Zelle den Knoten zwischengespeichert und in der updateItem Methode aktualisieren sollte:

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

Sie sollten eine setItem(...) Methode in der ConversationCellController definieren, die die Sicht aktualisiert (Sets Text auf Etiketten, usw. usw.) entsprechend.

+0

Hey, das ist eigentlich ein sehr guter Punkt! Genau wie der Android-ListView-Adapter mit dem ViewHolder-Muster, um die bereits generierten Ansichten zu verfolgen, um CPU-Auslastung und Verzögerungen zu reduzieren. Ich werde Ihre Idee so schnell wie möglich versuchen. Können Sie mir auch sagen, was ich tun kann, um Ereignisse mit meinem aktuellen Setup/Code auszulösen, so dass eine bestimmte Zelle aktualisiert wird? Ich habe einen Fortschrittsbalken für eine Nachricht, die gesendet wird, und ich möchte zum Beispiel seinen Fortschritt aktualisieren. Vielen Dank ! – Mackovich

+0

Für die Updates, bei denen es sich um eine andere Frage handelt, können Sie veranlassen, dass die Listenansicht ihre Zellen aktualisiert, indem sie JavaFX-Eigenschaften in Ihrer 'Message'-Klasse verwendet und die Elementliste mit einem Extraktor erstellt. Siehe http://stackoverflow.com/questions/23822550/ –

+0

danke. Ich werde nachsehen ! Übrigens habe ich Ihre Lösungen implementiert und es funktioniert gut. Ich hatte nicht viele Artikel (nur ein paar) in meiner ObservableList, aber ich sehe schneller aus! Wie auch immer, ich habe auch bemerkt, dass 'ccc.setItem' mehrere Male aufgerufen wurde, also musste ich einen Booleschen Wert in' ConversationCellController' hinzufügen, um zu vermeiden, dass die Ansichten zu viele Zeit gesetzt werden. War ich auch richtig? Für jetzt funktioniert es aber ich frage mich ... danke! – Mackovich