JavaFX jak wypełnić tabelę w odpowiedni sposób

0

Mam taką tabelę w ms sql:
user image
Mam klasę guide:

public class guide {

    private String kanal;
    private String program;
    private LocalTime godzina;
(...)

Oraz klasę gui:

public class gui extends Application {

    Stage stage;
    Scene sceneGlowna;

    int liczbaKanalow;
    List<String> glownaKanaly;
    @Override
    public void start(Stage primaryStage) throws Exception {

        TableView<guide> tableGlowna = new TableView<>(wczytajGlowna());

        List<TableColumn<guide, String>> colKanalList = new ArrayList<>();
        for (int i = 0; i < liczbaKanalow; i++) {
            colKanalList.add(new TableColumn<guide, String>(glownaKanaly.get(i)));
        }

        for(int i = 0; i < colKanalList.size(); i++){
            colKanalList.get(i).setCellValueFactory(new PropertyValueFactory<guide, String>("program"));
        }

        for(int i = 0; i < colKanalList.size(); i++)
            tableGlowna.getColumns().add(colKanalList.get(i));


        BorderPane paneglowna = new BorderPane(tableGlowna);
        sceneGlowna = new Scene(paneglowna);

        stage = primaryStage;
        stage.setScene(sceneGlowna);
        stage.setTitle("ProgramTV");
        stage.show();
    }

    public ObservableList<guide> wczytajGlowna(){
        ObservableList<guide> glownaList = FXCollections.observableArrayList();
        int iloscKanalow = 0;
        try {
            ObslugaDB dbGlowna = new ObslugaDB();
            Connection myConnection = dbGlowna.polacz_z_baza();
            String query = "SELECT * FROM glowna";
            ResultSet glowaFromDB = dbGlowna.getFromSQL(myConnection, query);
            String tempKanal = "";
            glownaKanaly = new ArrayList<>();
            while (glowaFromDB.next()) {
                if(glowaFromDB.getString(3).toString() != null && !glowaFromDB.getString(3).toString().isEmpty()) {
                    if(!glowaFromDB.getString(2).equals(tempKanal)){
                        iloscKanalow++;
                        tempKanal = glowaFromDB.getString(2);
                        glownaKanaly.add(tempKanal);
                    }
                    glownaList.add(new guide(glowaFromDB.getString(2), glowaFromDB.getString(3), new LocalTime(glowaFromDB.getString(4))));
                }
            }
        } catch(Exception ex){
            ex.printStackTrace();
        }
        liczbaKanalow = iloscKanalow;
        return glownaList;
    }
}

Z racji, iż pobieram każdy program z całej listy zwróconej przez metodę wczytajGlowna() dla każdej kolumny, tutaj:

for(int i = 0; i < colKanalList.size(); i++){
            colKanalList.get(i).setCellValueFactory(new PropertyValueFactory<guide, String>("program"));
        }

Logicznym efektem działania jest taki wynik:
user image
Jednak chciałbym wyświetlać w odpowiedniej kolumnie filmy tylko dla odpowiedniego kanału, to znaczy dla kolumny TV 4 mieć tylko filmy CSI:Kry(...),Grudge(...) oraz Kochaj(...), odpowiednio dla każdego innego kanału/kolumny. Próbowałem napisać swój callback mniej więcej, tak:

colKanalList.get(i).setCellValueFactory(new Callback<TableColumn.CellDataFeatures<guide, String>, ObservableValue<String>>() {

                @Override
                public ObservableValue<String> call(TableColumn.CellDataFeatures<guide, String> p) {
                    if (p.getValue().getKanal().equals(/*colKanalList.get(i)*/)) {
                        return new SimpleStringProperty(p.getValue();
                    } else {
                        return new SimpleStringProperty("<no name>");
                    }
                }
            });

Tyle, że wykomentowany kawałek jest niedozwolony (i musiało by być stałe). Próbowałem nawet inaczej, aby każdą kolumnę generować osobno, kolejno jedna po drugiej, ale to też okazało się źle, bo cały czas kręcę się wokół jednej ObservableList<guide>. Musi być jakiś sposób zdefiniowania własnego callbacka, ale cały czas mi nie wychodzi, stąd mój post, może tutaj ktoś byłby w stanie mi pomóc, będę bardzo wdzięczny za każdą wskazówkę.

1

To tak nie zadziała.
Tableiew jest przeznaczone do wyświetlania w jednym wierszu danych z jednego obiektu przechowywanego w ObservableList. Siłą rzeczy musisz wypełnić tę listę obiektami, z których da się renderować wszystkie kolumny dla całego wiersza.
Opcje masz więc takie:

  • Zmienić implementację swojej klasy guide (swoją drogą nazwy klas zaczynamy wielką literą) np. na taką, która przechowuje program wszystkich stacji dla każdej godziny i z niej wyświetlać poszczególne wiersze.
  • Zastąpić TableView kilkoma ListView, po jednym dla każdego programu i każdy z nich wyświetlać oddzielnie. Jeżeli umieścisz je w panelu jeden obok drugiego i odpowiednio sformatujesz to uzyskasz porównywalny efekt.
  • Napisać własną implementację TableView, która wyświtli dane np z modelu przechowywango w formie siatki (np. dwuwymiarowej tablicy).

Nie widzę sposobu na wyświetlenie tak zorganizowanych danych w TableView

0

Czy w przypadku pierwszej z opcji, mogłoby to wyglądać tak:

public class Guide {
private LocalTime godzinaProgramow;
private List<List<String>> program;
}

wówczas składowa godzinaProgramow przechowywała by np. godzinę 15 (jeden z wierszy kolumny), zaś lista list program byłaby taka:user image, wówczas każdy element z listy (czyli kolejna lista, przedstawiająca program dla każdego z kanałów) wypełniałby każdą kolumnę wiersza (odpowiadającego za godzinę 15). Czy coś takiego mogłoby być ? bo nie wiem czy mniej więcej o takie coś chodziło w pierwszej z sugestii.

1

Mniej więcej o coś takiego mi chodziło.
Ogólnie tak czy inaczej TableView średnio się sprawdzi w tym przypadku. Programy zaczynają się z dokładnością do 5 minut, jeżeli będziesz miał kilka programów zaczynających się w obrębie tej samej godziny np. 14:05 i 14:55 to musisz obydwa umieścić w tym samym wierszu a program, który trwa dłużej np. ponad dwie godziny pozostawi jakiś wiersz niewypełniony. JavaFX na chwilę obecną nie daje możliwości mergowania poszczególnych komórek więc wyglądało to będzie słabo.

Zastanów się nad stworzeniem własnego komponentu, który potrafiłby mądrze to wyświetlić. Na dobrą sprawę to możnaby po prostu rysować kwadraty o wysokości odpowiadającej długości programu i w ten sposób uzyskać fajny efekt.

0

Zmieniłem klasę Guide tak, że teraz ObservableList zawiera obiekty przedstawiające wiersze. Każdy taki obiekt zawiera składową

private List<List<String>> program;

tylko pytanie czy mając taką listę, mogę wskazać aby pierwsza kolumna wiersza tabeli miała wartość pierwszej listy list, druga kolumna miała drugą wartość listy list itd. czyli coś takiego:
user image

1

Rzeźba w brązie to będzie straszna i nie będzie to kawałek ładnego kodu ale wyglądać to może mniej więcej tak:

firstColumn.setCellValueFactory(new Callback<CellDataFeatures<Guide,String>, ObservableValue<String>>() {
	public ObservableValue<String> call(CellDataFeatures<Guide, String> cfd) {
        return new ReadOnlyObjectWrapper<String>(cfd.getValue().getCellValueForIndex(0));
	}
});

i takie coś dla każdej kolejnej kolumny w tabeli. Dodatkowo w klasie Guide będzie potrzebna metoda, która zwróci wartość z tej Twojej listy list dla poszczególnych kolumn. Tu masz przykładową implementację która po prostu dodaje Stringi w tej liście zakladając, że pierwsza lista Stringów w liście list Stringów zawiera programy dla danej godziny dla programu wyświetlanego w pierwszej kolumnie. Pisane z palca bez sprawdzania więc może nie działać:

public String getCellValueForIndex(int columnIndex) {
    List<String> oneProgram = program.get(columnIndex);
    StringBuilder sb = new StringBuilder();
    for (String s : oneProgram) {
        sb.append(s).append("\n");
   
    }  
    return sb.toString();
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1