13.02.07 Tabellen

Eine weitere wichtige, aber auch kompilzierte Komponente aus der Swing-Familie ist die JTable. Dabei handelt es sich, wie der Name schon vermuten lässt, um eine Klasse (zumindest im Vordergrund), die Daten in einer Tabelle anordnet. In diesem Kapitel werden Sie lernen, wie man Tabellen nutzt und für die eigenen Zwecke anpasst.

Die Arbeitsweise der JTable

Auch die JTable arbeitet mit dem zuvor beschriebenen Schema, Daten und Grafik voneinander zu trennen. Für die Daten steht dabei die Schnittstelle TableModel zur Verfügung. Wer nicht alle Methoden neu implementieren möchte, kann das DefaulTableModel verwenden, welches die Daten als Vektoren von Vektoren speichert, doch dazu später mehr.

Die erste Tabelle

Das einfachste ist eine Tabelle direkt über den Konstruktor zu befüllen. Dazu rufen Sie den Konstruktor JTable(Object[][] rowData, Object[] columnNames) auf. In rowData stehen dabei die Daten, columnNames gibt die Spaltennamen an.
Die Tabelle kann aber nicht so einfach auf einen JFrame gesetzt werden, Sie müssen sie vorher in ein JScrollPane packen, da sonst die Spaltennamen nicht angezeigt werden. Hier ein einfaches Beispiel:

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
JFrame frame = new JFrame();
frame.setBounds(100,100,500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] r1 ={"Max", "Mustermann"};
String[] r2 = {"Erika", "Mustermann"};
String[] r3 ={"Franz-Xaver", "Gabler"};
String[][] data = {r1, r2, r3};
String[] names = {"Vorname","Nachname"};
JTable table = new JTable(data, names);
JScrollPane pane = new JScrollPane(table);
frame.add(pane);
frame.setVisible(true);

Sollen die Daten erst im Laufe des Programmes hinzugefügt werden, muss das Datenmodel angesprochen werden. Wenn im Konstruktor kein Model expilzit angegeben wird, dann wird das DefaulTabelModel verwendet. Daher muss der Wert von jTable.getModel() gecastet werden.
Anschließend stehen unter anderen die Methoden addRow(Object[] row), addColumn(Object columnName) und removeRow(int row) zur Verfügung. Die Veränderungen werden automatisch an einen TableModelListener gesendet, der dafür sorgt, dass die Neuerungen auch gezeichnet werden.

Daten editieren

Die Daten einer Tabelle können vom Benutzer editiert werden, die veränderten Werten werden dann auch sofort ins Model eingetragen, sofern das verwendete Model das unterstützt. Das DefaultTableModel tut dies, bei Ihren eigenen Modellen müssen Sie es selbst implementieren.
Die Werte können dann so abgerufen werden:

jTable.getModel().getValueAt(int rowIndex, int columnIndex);

Es gibt zwar auch die Methode jTable.getValueAt(int row, int column), diese richtet sich aber nach der angezeigten Spaltenreihenfolge, nicht nach der ursprünglich im Model festgelegten. Die Spaltenreihenfolge kann vom Nutzer geändert werden, wenn Sie ihm dies nicht ausdrücklich durch

table.getTableHeader().setReorderingAllowed(false);

verbieten.

Erweiterte Zelleninhalte

Normerlerweise wird der Tabelleninhalt immer in reinem Text angegeben, doch manchmal ist es wünschenswert, eine bestimmte Spalte farblich hervorzuheben oder gar einen Button in die Zelle zu setzen. Dafür müssen Sie das Interface TableCellRenderer implementieren. Über die Methode getTableCellRendererComponent(...) holt sich die Tabelle, sobald eine bestimmte Zelle gezeichnet werden soll, die entsprechende Komponente. Wollen Sie einen Button haben, müssen Sie an dieser Stelle einen Button zurückgeben.
Die obige Methode bringt Ihnen folgende Parameter mit, mit denen Sie das Aussehen und den Inhalt Ihres Buttons beeinflussen können:

  • JTable table: Die verwendete Tabelle. Damit können Sie Farben sowie das Modell oder Zeilengrößen ermitteln.
  • Object value: Der Wert, der in der betroffenen Zelle stehen soll.
  • boolean isSelected: Ob die aktuelle Zelle selektiert ist.
  • hasFocus: Ob die Tabelle zurzeit fokussiert ist.

Neben diesen Parametern gibt es noch die Werte row und column, die angeben, um welche Zelle es sich im Model handelt. Folgender Code zeigt, wie Sie in eine Zelle einen Tooltip-Text einbauen können:

package de.jbb.renderer

import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class ToolTipRenderer extends JLabel implements TableCellRenderer

  public ToolTipRenderer(){
    setOpaque(true);
  }

  @Override
  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

    if (isSelected) {
      setBackground(table.getSelectionBackground());
      setForeground(table.getSelectionForeground());
    } else {
      setBackground(table.getBackground());
      setForeground(table.getForeground());
    }   
    setFont(table.getFont());
    if(value!=null){
      setText(value.toString());
    }
    setToolTipText(getText());
    return this;
  }

}

Nun muss der entsprechende Renderer noch der Tabelle zugewiesen werden. Soll er für alle Spalten gelten, können Sie ihn wie folgt zuweisen:

jTable.setDefaultRenderer(String.class, new ToolTipRenderer());

Wenn er dagegen nur in einer bestimmen Spalte angezeigt werden soll, legen Sie das über eine TableColumn fest:

jTable.getColumn(columnName).setCellRenderer(new ToolTipRenderer());

Der Parament columnName kann ein beliebiges Objekt sein, doch damit die gefunden werden kann muss die Spalte mit dem selben Objekt erzeugt worden sein, bzw. mit einem Objekt, das mit dem Aufruf von equals(Object o) wieder gefunden werden kann. Eine sichere Methode ist demnach das Verwenden von Strings, wenn Sie nicht equals(Object o) überschreiben wollen.

Tabellenspalten sortieren

Nicht immer ist es möglich, die Einträge einer Tabelle in der richtigen Reihenfolge hinzuzufügen, davon abgesehen, kann der Benutzer auch eine andere Ansicht von der „richtigen“ Reihenfolge haben. Daher ist es sinnvoll, dass Ihre Tabellen einen TableRowSorter verwenden, der dafür sorgt, dass die Zeilen richtig geordnet werden. Einen TableRowSorter hinzuzufügen ist einfach:

TableRowSorter sorter = new TableRowSorter();
sorter.setModel(jTable.getModel());
jTable1.setRowSorter(sorter);

Jetzt kann der Benutzer durch einen Klick auf einen Spaltenkopf die Tabelle entsprechend ordnen. Wenn Sie nichts anderes definiert haben, werden die Zelleninhalte in Strings umgewandelt und nach dem Alphabet geordnet. Sie können aber eigene Comperator-Instanzen hinzufügen. Wie Sie Comperator-Klassen schreiben können Sie in Kapitel D) Objekte sortieren -Comparator und Comparable nachlesen. Hinzugefügt wird der Comperator dann für jede Spalte einzeln:

sorter.setComperator(0, new IntegerComperator()); // imagined class;
sorter.setComperator(4, new PointComperator()); // imagined class 

Spalten, die Sie nicht extra definieren werden weiterhin als String behandelt.

Ein eigenes TableModel verwenden

Von Zeit zu Zeit kann es sinnvoll sein, ein eigenes TableModel zu implementieren um zum Beispiel auch mit Objekten in der Tabelle hantieren zu können. Hierzu implementieren Sie einfach TableModel aus dem Paket javax.swing.table. Die zu implementierenden Methoden werden im Folgenden erklärt.
Die Methoden getColumnCount() und getRowCount() geben die Anzahl der Spalten bzw. der Zeilen zurück. Über getColumnName(int columnIndex) erfragen sich die Listener den Spaltennamen einer bestimmten Spalte. Diese Listener werden über addTableModelListener(TableModelListener listener) von u. a. der JTable angemeldet und mit removeTableModelListener(TableModelListener listener) beizeiten wieder gelöscht. Eine wichtige Methode ist getColumnClass(int columnIndex), denn sie liefert dem Aufrufer die Klasse, deren Objekte in dieser Spalte vorkommen.
Dann gibt es noch die Methode setValueAt(int row, int col), über die der Aufrufer den Wert einzelner Zellen im Modell speichern kann. Dies passiert beispielsweise, wenn eine Zelle editiert wurde. Wenn Sie nicht wollen, dass die Zellen editiert werden können, setzen Sie den Rückgabewert der Methode isCellEditable(int row, int col) auf false. Damit die Daten angezeigt werden ruft die für die Grafik zuständige Methode die Methode getValueAt(int row, int col) auf, die den Wert ensprechend zurückgibt.

Beim Implementieren dieser Methoden müssen Sie sich auch um den Aufruf der Listener kümmern, da sonst niemand etwas von den Veränderungen mitbekommt.
Damit haben Sie alle obligatorischen Methoden implementiert. Dennoch ist es sinnvoll, zumindest eine zusätzliche Methode addRow(...) zu definieren, die ein Objekt Ihrer Wahl annimmt.

Auf der nächsten Seite finden Sie ein Beispiel für ein TableModel, das für den Einsatz von Point gedacht ist.

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.