Java FX TableView

Willemers Informatik-Ecke

Das einzig seriöse Javabuch :-) Mehr...


Bei Amazon bestellen

Ferien an der Flensburger Förde
Falls ich Ihnen helfen konnte:!
Vielen Dank!!!
2017-04-06
Eine Tabelle wird in Java FX durch die Klasse TableView realisiert. Wie schon bei ListView und ComboBox wird eine ObservableList zum Datenlieferant.
TableView<Spieler> table = new TableView<Spieler>();
ObservableList<Spieler> data = FXCollections.observableArrayList(
    new Spieler("Batman", 0),
    new Spieler("Superman", 0),
    new Spieler("Spiderman", 0),
    new Spieler("Gaston", 100),
    new Spieler("Goofy", 0));
table.setItems(data);
Die Highscore-Tabelle besteht aus zwei Spalten, dem Spielernamen und der Punktzahl. Eine Spalte wird durch die Klasse TableColumn realisiert. Die Spalten erhalten als Generics einmal die Klasse Spieler, aber zusätzllich den Typ der aufgezeichneten Spalte. Beim Namen ist dies String, bei den Punkten ist das Integer, weil int in den Generics nichts verloren hat.
TableColumn<Spieler, String> spalteName
        = new TableColumn<Spieler, String>("Name");
spalteName.setMinWidth(100); // Mindestbreite setzen
spalteName.setCellValueFactory(
        new PropertyValueFactory<Spieler, String>("name"));
// Die Spalte wird in die Tabelle gehängt.
table.getColumns().add(spalteName);
Mit der CellValueFactory wird dafür gesorgt, dass der Inhalt der Spalte auch dargestellt wird. Der Parameter des Konstruktors der Klasse PropertyValueFactory muss mit dem Namen der Getter-Methode übereinstimmen. Bei name muss es also eine Methode getName zum Ermittel des Spalteninhalts geben.

Schließlich muss natürlich irgendwann die Spalte in die Tabelle eingefügt werden.

Sortierter HighScore

Eine Highscore-Tabelle muss natürlich sortiert sein. Zum Glück kennt die ObservableList auch die Methode sort. Allerdings will sie dafür einen Comparator haben, der erzählt, wie Elemente der Klasse Spieler sortiert werden sollen. Das funktioniert wie bei allen anderen Java-Collections.
Comparator<Spieler> scoreVergleicher = new Comparator<Spieler>() {
    public int compare(Spieler orig, Spieler vgl) {
        int oPkte = orig.getPunkte();
        int vPkte = vgl.getPunkte();
        if (oPkte < vPkte)
            return 1;
        if (oPkte > vPkte)
            return -1;
        return 0;
    }
};
// Nun kann sort mit scoreVergleicher aufgerufen werden:
data.sort(scoreVergleicher);
Änderungen an der ObservableList schlagen sich sofort auf die TableView durch, weil die List das Datenmodell für die TableView darstellt.

Editierbare Tabelle

Die Inhalte einer TableView sind für den Anwender durch Doppelklick änderbar, wenn der Programmierer die Tabelle für editierbar erklärt. Leider reicht die einfache Erklärung nicht. Jede Spalte muss eine CellFactory anbieten und das CellEditEvent fangen.
spalteName.setCellFactory(TextFieldTableCell.forTableColumn());
spalteName.setOnEditCommit(
    new EventHandler<CellEditEvent<Spieler, String>>() {
        @Override
        public void handle(CellEditEvent<Spieler, String> t) {
            ((Spieler) t.getTableView().getItems().get(
                    t.getTablePosition().getRow())
                    ).setName(t.getNewValue());
        }
    }
);
// Die Tabelle muss zum Editieren freigeschaltet werden.
table.setEditable(true);
Etwas anders läuft es, wenn Sie die Punkte ändern wollen. Das war ein besonderer Wunsch von Batman. Ich kann mir allerdings nicht vorstellen, dass Batman schummeln wollte.
spaltePunkte.setCellFactory(TextFieldTableCell.<Spieler, Integer>
        forTableColumn(new IntegerStringConverter()));
spaltePunkte.setOnEditCommit(
    new EventHandler<CellEditEvent<Spieler, Integer> >() {
        @Override
        public void handle(CellEditEvent<Spieler, Integer> evt) {
            Integer neuWert = evt.getNewValue();
            evt.getTableView().getItems().get(
                evt.getTablePosition().getRow())
                .setPunkte(neuWert);
            // Punktzahl wurde geändert: neu sortieren!
            data.sort(scoreVergleicher);
        }
    }
);

Alles zusammen

import java.util.Comparator;

import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.converter.IntegerStringConverter;
 
public class FxTable extends Application {

    @Override
    public void start(Stage stage) {
        TableView<Spieler> table = new TableView<Spieler>();
        ObservableList<Spieler> data =
                FXCollections.observableArrayList(
                new Spieler("Badman", 120),
                new Spieler("Sabberman", 80),
                new Spieler("Spinnerman", 0),
                new Spieler("Captain Alergica", 100),
                new Spieler("Booman", 0));
        
        table.setItems(data);
        table.setEditable(true);
 
        TableColumn<Spieler, String> spalteName 
                = new TableColumn<Spieler, String>("Name");
        spalteName.setMinWidth(100);
        // Der String im Konstruktor von PropertyValueFactory
        // muss mit der get-Methode von Spieler korrespondieren
        spalteName.setCellValueFactory(
            new PropertyValueFactory<Spieler, String>("name"));
        spalteName.setCellFactory(TextFieldTableCell.forTableColumn());
        // Spalte soll änderbar sein
        spalteName.setOnEditCommit(
            new EventHandler<CellEditEvent<Spieler, String>>() {
                @Override
                public void handle(CellEditEvent<Spieler, String> t) {
                    ((Spieler) t.getTableView().getItems().get(
                            t.getTablePosition().getRow())
                            ).setName(t.getNewValue());
                }
            }
        );

        // Sortieren nach der Highscoreliste
        Comparator<Spieler> scoreVergleicher = new Comparator<Spieler>() {
            public int compare(Spieler orig, Spieler vgl) {
                int oPkte = orig.getPunkte();
                int vPkte = vgl.getPunkte();
                if (oPkte < vPkte)
                    return 1;
                if (oPkte > vPkte)
                    return -1;
                return 0;
            }
        };
        data.sort(scoreVergleicher);
        
        TableColumn<Spieler, Integer> spaltePunkte
                = new TableColumn<>("Punkte");
        spaltePunkte.setMinWidth(100);
        spaltePunkte.setCellValueFactory(
                new Callback<CellDataFeatures<Spieler, Integer>,
                ObservableValue<Integer>>() {
            @Override 
            public ObservableValue<Integer> call(
                    CellDataFeatures<Spieler, Integer> c) {
                return new SimpleIntegerProperty(c.getValue().
                        getPunkte()).asObject();
            }
        });
        spaltePunkte.setCellFactory(
                TextFieldTableCell.<Spieler, Integer>forTableColumn(
                new IntegerStringConverter()));
        spaltePunkte.setOnEditCommit(
            new EventHandler<CellEditEvent<Spieler, Integer> >() {
                @Override
                public void handle(CellEditEvent<Spieler, Integer> evt) {
                    Integer neuWert = evt.getNewValue();
                    evt.getTableView().getItems().get(
                        evt.getTablePosition().getRow())
                        .setPunkte(neuWert);
                    // Punktzahl wurde geändert: neu sortieren!
                    data.sort(scoreVergleicher);
                }
            }
        );
 
        table.getColumns().add(spalteName);
        table.getColumns().add(spaltePunkte);
 
        Scene scene = new Scene(table);
        stage.setScene(scene);
        stage.show();
        stage.setWidth(450);
        stage.setHeight(550);
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}


Homepage - Java - Java FX (C) Copyright 2017 Arnold Willemer