Swing: JTable

Dieses Beispiel zeigt, wie ein JTable angelegt wird.
Das Projekt befindet sich unter "SwingJTable" in Gitlab: https://gitlab.cs.hs-rm.de/knauf/swt-ws19-knauf/tree/master/SwingJTable

Der Code ist auch hier zu finden: SwingJTable.zip

Erzeugen der Tabelle

Wie bei der JList aus dem vorherigen Tutorial muss auch hier zuerst ein "Scroll Pane" auf dem Fenster platziert werden. Auf dieses wird ein "Table" gesetzt:
JTable hinzufügen


Ich konnte die erzeugte Tabelle im Designer nachträglich nicht mehr anklicken, es wurde immer das ScrollPane ausgewählt.

Lösung: links unten befindet sich ein Fenster "Navigator". Dort kann man sich durch die Control-Hierarchie klicken und dort die Tabelle auswählen. Jetzt ist sie im Designer-Fenster gewählt und auch rechts unten in den Properties.

Befüllen der Tabelle

Ich will in der Tabelle Objekte vom Typ Student anzeigen, wobei die Spalten "Matrikelnummer", "Nachname" und "Vorname" angezeigt werden sollen.

Wie beim vorherigen ComboBox- und List-Beispiel wurde ein Default-Model generiert, das uns hier nur stört.
Hier der Auszug aus initComponents:
        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
Dieser generierte Code zeigt, wie man ein Model auf Basis eines zweidimensionalen Arrays erzeugen kann.

Dieses Model muss zuerst zurückgesetzt werden. Dazu die Tabelle wählen und in den Properties bei "model" per Rechtsklick die Funktion "Restore Default Value" aufrufen. Die gleiche Funktion habe ich im CombBox-Beispiel über den Ellipsis-Button des "Properties"-Fensters erreicht.
Reset model

Jetzt legen wir uns ein eigenes TableModel an, das eine Liste von Student-Objekten enthält.

public class TableModelStudent extends javax.swing.table.AbstractTableModel {

    private List<Student> studenten;

    public TableModelStudent(List<Student> studenten) {
        this.studenten = studenten;
    }
	
    public Student getStudentAt (int index)
    {
        return this.studenten.get(index);
    }   
Die Klasse ist eine Subklasse von javax.swing.table.AbstractTableModel. Im Konstruktor wird ihr eine Liste von Studenten übergeben - man könnte sie aber auch mit einer Methode setStudenten von außen setzen. Außerdem habe ich die Methode getStudentAt zugefügt - diese wird weiter unten beim Abrufen der Auswahl nötig sein.

Wir müssen diverse Methoden aus AbstractTableModel implementieren:

    @Override
    public String getColumnName(int column) {
        switch (column) {
            case 0:
                return "Matrikelnummer";
            case 1:
                return "Nachname";
            case 2:
                return "Vorname";

            default:
                throw new IllegalArgumentException("Spaltenindex " + column + " ist ungültig");
        }
    }

    @Override
    public int getRowCount() {

        return this.studenten.size();
    }

    @Override
    public int getColumnCount() {
        return 3;
    }

    @Override
    public java.lang.Class<?> getColumnClass(int column) {
        switch (column) {
            case 0:
            case 1:
            case 2:
                return String.class;
            default:
                throw new IllegalArgumentException("Spaltenindex " + column + " ist ungültig");
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Student studi = this.studenten.get(rowIndex);

        switch (columnIndex) {
            case 0:
                return studi.getMatrikelnummer();
            case 1:
                return studi.getNachname();
            case 2:
                return studi.getVorname();
            default:
                throw new IllegalArgumentException("Spaltenindex " + columnIndex + " ist ungültig");
        }
    }
Diese Methoden werden allesamt vom JTable aufgerufen, um die Tabelle zu befüllen.
Im Detail:
Dieses TableModel wird im Konstruktor meines Fensters erzeugt und in die Tabelle gesteckt:
        ArrayList<Student> listData = new ArrayList<Student>();
        Student stud1 = new Student();
        stud1.setMatrikelnummer(1234567);
        stud1.setVorname("Hans");
        stud1.setNachname("Meier");
        listData.add(stud1);

        ...

        //Erzeugen des TableModell:
        this.modelStudenten = new TableModelStudent(listData);
        //Befüllen der Tabelle:
        this.jTableStudenten.setModel(modelStudenten);


Auswahl

Ein JTable unterstützt im Default Mehrfachauswahl (mit "Strg+Click" kann man beliebig viele Zeilen wählen). In den "Properties" kann z.B. auf Einfachauswahl umgestellt werden:
Selection model

Das Beispielprogramm lauscht auf eine Auswahländerung der Tabelle und zeigt die gewählten Datensätze in einem Textfeld an. Für die Auswahländerung gibt es leider kein Event im JTable, das wir per Designer registrieren könnten. Stattdessen muss man das selbst programmieren:
        this.jTableStudenten.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                jTableStudentenSelectionChanged(e);
            }
        });
Die Methode jTableStudentenSelectionChanged sieht so aus:
    private void jTableStudentenSelectionChanged (ListSelectionEvent e)
    {
        String studentenSelected = "";
        
        int[] selectedRows = this.jTableStudenten.getSelectedRows();
        for(int rowSelected : selectedRows)
        {
            Student student = this.modelStudenten.getStudentAt(rowSelected);
            if (studentenSelected.length() > 0)
            {
                studentenSelected += ", ";
            }
            studentenSelected += student.toString();
        }
        this.jTextFieldSelected.setText(studentenSelected);
    }
Der JTable liefert uns nur die gewählten Zeilen-Indizes (getSelectedRows). Anhand dieser Indizes müssen wir die Studenten aus dem Model holen (über getStudentAt).


Umgekehrt können wir die Auswahl so setzen (das macht der Button "Müller wählen"):
        //...Student namens "Müller" in TableModel suchen...

        this.jTableStudenten.getSelectionModel().setSelectionInterval(indexMueller, indexMueller);

Stand 27.11.2019
Historie:
22.11.2019: Erstellt
27.11.2019: Screenshot für SelectionMode korrigiert.