2016-04-15 22 views
17

Jestem trochę nowy w Javie, JavaFX i programowaniu w ogóle, i mam problem, który łamie mój mózg.Jak wypełnić okno listy w JavaFX przy użyciu obiektów niestandardowych?

W większości tutoriali Mam spojrzał dotyczące wypełniania ListView (Używanie ObservableArrayList dokładniej) najprostszym sposobem na to jest, aby go z ObservableList strun, tak jak poniżej:

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

Ale nie chcę używać ciągów. Chciałbym użyć obiektu niestandardowego zrobiłem nazwie słowa:

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

Każdy obiekt Słowo tylko ma 2 własności: wordString (ciąg tego słowa) i definicji (inny ciąg znaków, który jest definicja tego słowa). Mam pobierające i ustawiające dla obu.

Możesz zobaczyć, gdzie to idzie - kod się kompiluje i działa, ale kiedy wyświetlam go w mojej aplikacji, zamiast wyświetlać tytuły każdego słowa w ListView, wyświetla sam obiekt Word jako String!

Image showing my application and its ListView

tutaj moje pytanie jest, w szczególności, czy istnieje prosty sposób przerobić to:

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

W taki sposób, że zamiast podejmowania Words bezpośrednio od wordsList, to dostęp do wordString właściwość w każdym słowie mojej obserwowalnej listy kontrolnej?

Dla jasności, to nie jest dla Androida, a lista słów zostanie zmieniona, zapisana i załadowana ostatecznie, więc nie mogę po prostu zrobić kolejnej tablicy, która będzie zawierała słowaStrings. Zrobiłem trochę badań w sieci i wydaje mi się, że istnieje coś, co nazywa się "fabrykami komórek", ale wydaje się niepotrzebnie skomplikowane, co wydaje się być tak prostym problemem i jak już wcześniej wspomniałem, jestem trochę początkujący, jeśli chodzi o programowanie.

Czy ktoś może pomóc? To jest mój pierwszy raz tutaj, więc przepraszam, jeśli nie zawarłem wystarczającej ilości kodu lub zrobiłem coś złego.

Odpowiedz

20

Podejście Rozwiązanie

Radzę użyciu cell factory, aby rozwiązać ten problem.

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

Przykładowa aplikacja

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

Realizacja Uwagi

Chociaż można zastąpić toString w klasie Worda, aby zapewnić reprezentację ciąg słowem, którego celem jest reprezentacja w twoim ListVie w, Polecam dostarczenie fabryki komórek w ListView do ekstrakcji danych widoku z obiektu słowa i reprezentacji go w swoim ListView. Korzystając z tego podejścia, uzyskuje się oddzielenie problemów, ponieważ nie wiąże się to z graficznym widokiem obiektu Word z jego tekstową metodą toString; więc toString może nadal mieć różne wyniki (na przykład pełne informacje o polach Worda z nazwą słowa i opisem do celów debugowania). Ponadto fabryka komórek jest bardziej elastyczna, ponieważ można zastosować różne węzły graficzne, aby utworzyć wizualną reprezentację danych poza zwykłym ciągiem tekstowym (jeśli chcesz to zrobić).

Ponadto, na marginesie, polecam wykonanie obiektów Word immutable objects, usuwając ich setery. Jeśli naprawdę potrzebujesz zmodyfikować obiekty słów we własnym zakresie, najlepszym sposobem na to jest uzyskanie widocznych obserwowalnych właściwości dla pól obiektów. Jeśli chcesz, aby twój interfejs aktualizował się, gdy zmieniają się obserwowalne właściwości twoich obiektów, musisz powiadomić swoje komórki list o zmianach powiązanych z nimi elementów, nasłuchując zmian w nich (co jest nieco bardziej skomplikowane w tym walizka). Zauważ, że lista zawierająca słowa jest już zauważalna i ListView zajmie się obsługą zmian na tej liście, ale jeśli zmodyfikowałeś definicję słowa na przykład w obrębie wyświetlanego obiektu słowa, to widok listy nie odbierałby zmian w definicja bez odpowiedniej logiki słuchacza w fabryce komórek ListView.

+0

Bardzo dziękuję za bardzo szczegółową odpowiedź! Na razie zamierzam spróbować przesłonić metodę toString(), ale zamierzam zrobić jeszcze więcej czytania na temat fabryk komórek, aby lepiej je zrozumieć, a następnie zmodyfikować aplikację tak, aby używała ich zgodnie z sugestią. Będę tylko modyfikował "definicję" poszczególnych słów za pomocą tego przycisku edycji, a widok listy śledzi tylko właściwość "słowo", więc nie musi śledzić zmian w "definicji". –

1

Założę się o niklu, że ListView wywołuje metodę toString() w programie Word. Jeśli go nie zastąpiłeś, używa on domyślnej metody toString(), która wyprowadza tylko ... mało przydatne informacje. Zastąp go, aby uzyskać ładnie sformatowany ciąg i powinieneś być gotowy!

+0

Hmmm, to ma sens! Dam ci ten strzał; teraz mam klasę do wzięcia udziału, ale wypróbuję ją później i zobaczę, czy to się uda. –

+0

Ale to nie jest dobre rozwiązanie: możesz chcieć, aby metoda 'toString()' zwracała coś innego niż to, co chcesz wyświetlać w interfejsie użytkownika. –

0

To, co aktualnie wyświetla ListView, to toString(). Aby rozwiązać problem wystarczy dodać następującą metodę w swojej klasie słowa (to tylko przykład):

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