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:

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.

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:
getRowCount liefert die Anzahl an Tabellenzeilen (also die Anzahl an Studenten in der Liste)
getColumnCount liefert die Anzahl an Tabellenspalten - hier fest "3"
getColumnName(index) liefert den Spaltennamen (bzw. den Anzeigetext in der Titelzeile) zurück.
getColumnClass(index) liefert den Datentyp für die Werte einer Spalte zurück. Hier habe ich nur String-Werte.
getValueAt(row, column) liefert den Wert für eine Tabellenzelle zurück. Hier muss anhand des Spaltenindex die entsprechende Property des Studenten abgerufen werden.
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:

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.