Erstellen einer Tabelle
Um JTable zu benutzen, muss es importiert werden. Zu Anfang sollte also die folgende Zeile stehen:import javax.swing.JTable;
Das Modell füllt die Tabelle
Das Hauptprogramm mit JTable
Das Hauptprogramm unterscheidet sich nur geringfügig.import javax.swing.JFrame; import javax.swing.JTable; import javax.swing.JScrollPane; public class TableTest extends JFrame { TableTest() { // Konstruktor setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JTable table = new JTable(new MeinTableModell()); add(new JScrollPane(table)); // ohne JScrollPane keine Titel! setSize(400, 300); setVisible(true); } public static void main(String[] args) { new TableTest(); // Table erzeugen } }
Erweiterung von AbstractTableModel
- Der Konstruktor von JTable verwendet nun eine Erweiterung (Ableitung) von AbstractTableModel übergeben, in diesem Fall MeinTableModell. Das TableModel sorgt das Füllen des JTable-Objekts mit Daten. Das JTable-Objekt wird bei der Darstellung das Modell befragen.
- Die Spaltenanzahl wird durch Überschreiben der Methode
getColumnCount festgelegt.
@Override public int getColumnCount() { return 2; }
- Die Spaltenüberschriften liefert die Methode getColumnName.
@Override public String getColumnName(int index) { // Die Spaltenueberschriften return index==0?"Interpret":"Titel"; }
- Die Zeilenzahl wird durch die Methode getRowCount festgestellt.
@Override public int getRowCount() { return daten.getAnzahl(); }
- Um die Inhalte zu ermitteln, ruft JTable die Methode getValueAt
auf und übergibt die Koordinaten.
@Override public Object getValueAt(int zeile, int spalte) { if (spalte==0) return daten.getInterpret(zeile); if (spalte==1) return daten.getTitel(zeile); return null; }
import javax.swing.table.AbstractTableModel; public class MeinTableModell extends AbstractTableModel { private DatenLieferant daten = null; // wo die Daten herkommen public MeinTableModell() { daten = new DatenLieferant(); // initialisiere Datenquelle } @Override public int getColumnCount() { // Anzahl der Spalten return 2; } @Override public String getColumnName(int arg0) { // Die Spaltenueberschriften if (arg0==0) return "Interpret"; if (arg0==1) return "Titel"; return null; } @Override public int getRowCount() { // Anzahl der Zeilen, also Datenobjekte return daten.getAnzahl(); } @Override public Object getValueAt(int zeile, int spalte) { // Die eigentlichen Daten if (spalte==0) return daten.getInterpret(zeile); if (spalte==1) return daten.getTitel(zeile); return null; } }
Implementierung von TableModel
Anstatt die Klasse AbstractTableModel zu erweitern könnte das Table-Modell natürlich auch das Interface TableModel implementieren. Zunächst sind die Unterschiede simpel. In den ersten Zeilen muss es heißen:
import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; public class MeinTableModell implements TableModel {
Der Compiler wird sich melden und darauf bestehen, dass es da unimplementierten Methoden gibt. Wenn man Eclipse oder Netbeans verwendet, werden diese mit einem Mausklick mit Default-Werten erzeugt.
JTable-Methoden
Im Zusammenspiel mit einem Swing-Programm werden Methoden von JTable verwendet. Als Beispiel wird in den Listings jeweils die Tabelle meinJTable verwendet.
Selektierte Zeilen in der Tabelle
Die aktuell selektierte Zeile wird über folgende Code-Sequenz ermittelt:if (meinJTable.getSelectedRowCount()==1) { int zeile = meinJTable.getSelectedRow();
In der ersten Zeile wird ermittelt, ob wirklich genau eine Zeile selektiert ist. Dann wird die Zeilennummer ermittelt.
Um Zeilen zu selektieren, wird zunächst jede Selektion aufgehoben. Dann wird bei jeder gewünschten Zeile die Selektion gewechselt. Im Beispiel befindet sich in der ArrayList<Integer> eine Liste der Zeilen, die selektiert werden sollen.
meinJTable.clearSelection(); for (int i=0; i<funde.size(); i++) { meinJTable.changeSelection(funde.get(i), WIDTH, true,false); }
Änderungen aktualisieren
Stellt das Programm fest, dass sich der Dateninhalt der Tabelle geändert hat, muss die Tabelle aktualisiert werden. Mit der Methode invalidate funktioniert dies nicht zuverlässig.Der Aufruf der Methode tableChanged erwirkt ein Neuladen der Daten von JTable. Wird als Parameter null übergeben, wird alles nachgeladen. Je nach Datenquelle kann der Aufwand natürlich recht hoch sein.
meinJTable.tableChanged(null); // funktioniert, ist aber recht grob
Wird nur eine Zeile geändert, kann JTable mit dem folgenden Aufruf dazu gebracht werden, nur diese eine Zeile zu aktualisieren, deren Nummer sich in der Variablen zeile befindet.
meinJTable.tableChanged(new TableModelEvent(meinJTable.getModel(), zeile));
Die beiden folgenden Aufrufe werden verwendet, um auf das Einfügen und Löschen von Zeilen zu reagieren.
meinJTable.tableChanged(new TableModelEvent(meinJTable.getModel(), 0, 0, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT )); meinJTable.tableChanged(new TableModelEvent(meinJTable.getModel(), zeile[i], zeile[i], TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ));
Eine einfache Lösung ist es, wenn man die Modell-Klasse durch den Aufruf von fireTableCellUpdated() einen Neuaufbau der Tabelle erzwingen lässt. Das kann beispielsweise in der Methode setValueAt() erfolgen.
public void setValueAt(Object value, int row, int col) { data[row][col] = value; fireTableCellUpdated(row, col); }
Spaltentypen
Jeder Spalte kann ein Typ zugewiesen werden, indem die Methode getColumnClass überschrieben wird. Der Parameter gibt an, welche Spalte angefragt wird. Die folgende Überschreibung sorgt dafür, dass alle Spalten Strings darstellen.
@Override public Class> getColumnClass(int spaltenindex) { return String.class; }
Wird für eine Spalte Integer.class zurückgegeben, wird die Spalte rechtsbündig ausgerichtet. Bei Boolean.class wird eine Checkbox angezeigt, mit einem Haken für true oder ohne Haken für false.
Die Klassen lassen sich in einem Array ablegen, deren Inhalt anhand der Spaltennummer zurückgegeben werden kann.
private final Class[] spaltenKlasse = {String.class, Integer.class, Boolean.class}; @Override public Class> getColumnClass(int col) { return spaltenKlasse[col]; }
Editieren in der Tabelle
Das TableModel kann so eingerichtet werden, dass die Werte innerhalb der Tabelle direkt veränderbar sind.- Die Methode isCellEditable liefert für jede Zelle den booleschen
Wert, ob sie editierbar sein soll.
@Override public boolean isCellEditable(int row, int col) { return true; }
- Nun können die Felder bereits angeklickt und verändert werden, allerdings
wird der Wert im Datenmodell noch nicht gespeichert. Dazu muss die
Methode setValueAt überschrieben werden, die die Objekte empfängt
und dann speichert.
@Override public void setValueAt(Object obj, int row, int col) { Gast gast = gaeste.get(row); if(0==col) { gast.setName((String) obj); } else if(1==col) { gast.setBesuche((Integer) obj); } else if(2==col) { gast.setRaucher((Boolean) obj); } }
import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.JScrollPane; import javax.swing.JFrame; import java.awt.BorderLayout; import java.awt.Dimension; public class EditRender extends JFrame { DasTableModel myModel = new DasTableModel(); public static void main(String[] args) { EditRender frame = new EditRender(); frame.pack(); frame.setVisible(true); } public EditRender() { super("EditRender"); JTable table = new JTable(myModel); table.setPreferredScrollableViewportSize(new Dimension(400, 200)); JScrollPane scrollPane = new JScrollPane(table); getContentPane().add(scrollPane, BorderLayout.CENTER); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } class DasTableModel extends AbstractTableModel { final String[] spaltenBeschriftung = { "String", "Double", "Integer", "Boole" }; final Object[][] data = { { "", new Double(0.0), new Integer(0), new Boolean(false) }, { "", new Double(0.0), new Integer(0), new Boolean(false) }, { "", new Double(0.0), new Integer(0), new Boolean(false) }, { "", new Double(0.0), new Integer(0), new Boolean(false) }, { "", new Double(0.0), new Integer(0), new Boolean(false) }, }; public int getColumnCount() { return spaltenBeschriftung.length; } public int getRowCount() { return data.length; } public String getColumnName(int col) { return spaltenBeschriftung[col]; } public Object getValueAt(int row, int col) { return data[row][col]; } public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } public boolean isCellEditable(int row, int col) { return true; } public void setValueAt(Object value, int row, int col) { data[row][col] = value; fireTableCellUpdated(row, col); System.out.println("setVal"+value); // da sind die Daten! } } }Interessant ist, dass die Titel der Tabelle nur erscheinen, wenn mit JScrollPane auch Schiebebalken für die Tabelle eingerichtet wurden.