8.6.9 | JTable |
Eine JTable stellt eine Tabelle in Swing dar. Eine Tabelle besteht aus den Spalten, deren Verhalten mittels der Klasse TableColumn eingestellt werden kann, sowie den Tabellenköpfen (JTableHeader), verschiedenen Models, Renderern und Editoren.
Material zum Beispiel
- Applet starten
- Quelltexte:
javax.swing.table.TableModel Das TableModel definiert die Daten, die die Tabelle anzeigt. Das Model liefert Aufschluss darüber, welche Werte eine Zelle hat, wobei es auch die Anzahl der Spalten und Zeilen definiert. Abhängig von Zeile und Spalte entscheidet das Model darüber, ob die Zelle editierbar ist, wobei der erfolgreich geänderte Wert mittels setValue(Object value) ins Model zurückgeschrieben wird. TableModel model = new AbstractTableModel() { String[][] data = new String[] { {"A1","A2"}, {"B1","B2"}}); public int getRowCount() { return data.length; } public int getColumnCount () { return 2; } public Object getValueAt (int row, int column) { return data[row][column]; } }Eine besondere Aufmerksamkeit verdient die Methode getColumnClass(int columnIndex). Diese definiert, welcher Klasse die Werte der übergebenen Spalte angehören. Durch diese Klasse entscheidet die Tabelle, welcher Renderer bzw. Editor für diese Zelle zu nehmen ist, je nachdem welcher Renderer bzw. Editor zu diser Klasse assoziiert wurde. Implementiert man beispielsweise nur die fehlenden Methoden des javax.swing.table.AbstractTableModel, so muss man diese Methode überschreiben, wenn ein anderer Renderer oder Editor als der für Strings benutzt werden soll.
javax.swing.table.TableColumnModel Das TableColumnModel kümmert sich um die Verwaltung der einzelnen Spalten (javax.swing.table.TableColumn) und darum, welche Spalte sich an welcher Position befindet (wenn der Benutzer die Reihenfolge der Spalten verändert hat) bzw. welche Spalte selektiert ist. Über die Exemplare der TableColumn-Klasse können die Spaltenbreiten und der Renderer für den Kopf (getHeaderRenderer()) in Erfahrung gebracht werden. ListSelectionModel Die Auskunft darüber, welche Zeile selektiert ist, liefert das ListSelectionModel (Zugriff über getSelectionModel() bzw. setSelectionModel(ListSelectionModel model)). Welche Spalte selektiert ist, liefert in der JTable das TableColumnModel.
Eine JTable benutzt standardmäßig ein Exemplar der Klasse javax.swing.table.DefaultTableCellRenderer, der von einem JLabel abgeleitet ist und dadurch neben Texten auch ein Icon anzeigen kann.
Das Setzen eines eigenen Renderers kann dabei über zwei Wege erfolgen. Zum einen kann man mithilfe der Methode setDefaultRenderer(Class valueClass, TableCellRenderer renderer) einen Renderer zu einer Klasse assoziieren (also bspw. einen Automobil-Renderer zur Klasse Automobil assoziieren, so dass alle Objekte dieser Klasse durch den passenden Renderer angezeigt werden). Zum anderen kann man in einer abgeleiteten Klasse die Methode getCellRenderer(int row, int column) überschreiben. Durch das Überschreiben hat man die Möglichkeit, einen Renderer je Zelle anzugeben. Liefert die Methode einen null-Wert zurück (der Standard), so wird der Renderer durch die Klasse des Wertes bestimmt.
Die einfachste Möglichkeit, einen eigenen Renderer für eine bestimmte Klasse zu schreiben, besteht darin, den DefaultTableCellRenderer zu erweitern und an die Superklasse einen passenden String als value-Parameter zu übergeben. Die Superklasse zeigt dann mittels toString() diesen Wert in einem JLabel an:
class MyTableCellRenderer extends DefaultTableCellRenderer { public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value instanceof Automobil) { Automobil automobil = (Automobil) value; value = "<html>Marke: "+automobil.getMarke()+ " / Modell: "+automobil.getModell()+"</html>"; } return super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); } }
Wie auch beim Renderer kann ein Editor zu einer Datenklasse assoziiert werden oder durch Überschreiben der Methode getCellEditor(int row, int column) pro Zelle definiert werden. Das zugrunde liegende Interface ist TableCellEditor (aus dem Paket javax.swing.table), welches das auch vom JTree benutzte Interface CellEditor erweitert.
Der Ablauf beim Editieren ist der folgende:
- Die Tabelle registriert sich als CellEditorListener mittels der Methode addCellEditorListener(CellEditorListener l) beim Editor.
- Wenn der Benutzer eine Zelle editieren will und diese gemäß dem TableModel editierbar ist, wird der Editor mit der Methode isCellEditable(EventObject) gefragt, ob dies gestattet ist.
- Wenn es erlaubt wurde, erfragt die Tabelle, ob die Zelle während des Editierens selektiert sein soll (Methode shouldSelectCell(EventObject anEvent)).
- Im Anschluss wird die Editierkomponente, die innerhalb der Zelle angezeigt werden soll, mittels der Methode getTableCellEditorComponent(JTable, Object, boolean isSelected, int row, int column) in der entsprechenden Zelle angezeigt. Der zu editierende Wert (value) wird dabei aus dem TableModel bezogen.
- Nun gibt es vier Möglichkeiten, wie der Editiervorgang beendet werden kann:
- Die Tabelle bricht den Editiervorgang ab und informiert den Editor über die Methode cancelCellEditing() darüber.
- Der Editiervorgang Tabelle möchte den Editiervorgang beenden. In diesem Fall wird die Methode stopCellEditing() aufgerufen. Liefert die Methode true zurück, wird der Editor beendet. Der Editor informiert die Tabelle über die CellEditorListener-Methode editingStopped(ChangeEvent e) darüber. Liefert die Methode hingegen false zurück, wird der aktuelle Editorvorgang nicht beendet.
- Wenn in der Editierkomponente die Eingabe abgebrochen wird (z.B. durch einen Druck auf die Escape-Taste), wird über das CellEditorListener-Interface die Tabelle über die Methode editingCanceled(ChangeEvent e) darüber informiert.
- Wenn die Eingabe erfolgreich war, informiert der Editor die Tabelle entsprechend. Dies erfolgt über die Methode editingStopped(ChangeEvent e).
- Wenn der Editiervorgang erfolgreich beendet wurde, erfragt die Tabelle den neuen Wert mittels der Methode getCellEditorValue() und schreibt ihn zurück in das TableModel.
// Die Daten als Array von // {Bild, Text, Text} Object[][] data = new Object[][] { {new ImageIcon(getClass().getResource("t1.gif")), "Neu", "Neues Dokument anlegen"}, {new ImageIcon(getClass().getResource("t2.gif")), "öffnen", "Vorhandenes Dokument öffnen"}, {new ImageIcon(getClass().getResource("t3.gif")), "Speichern", "Speichern des Dokumentes"} }; // Die Spaltennamen Object[] columnNames = new Object[] {"","Name", "Beschreibung"}; JTable table = new javax.swing.JTable(data, columnNames); // Für die erste Spalte (Name "") einen Renderer setzen table.getColumn ("").setCellRenderer( // Standard-Renderer erweitern new DefaultTableCellRenderer() { public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { // Label der Oberklasse erweitern JLabel label = (JLabel) super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); // Lediglich Text und Grafik anpassen if (value != null) { label.setText(""); label.setIcon((ImageIcon) value); } return label; } });