Löschen von Tabellendaten
Tabelle und Model
Die Datenverwaltung einer JTable erfolgt innerhalb eines
sog. Models. Es stellt eine Datenverwaltungsstruktur
dar, die alle Möglichkeiten kapselt, die
Tabellendaten zu erweitern, zu löschen, etc. Dies
beinhaltet insbesondere auch alle dort registrierten
Listener, etwa zur Aktualisierung der Tabellenansicht
bei Änderungen. Die JTable selbst,
stellt lediglich die optische Aufarbeitung der Daten,
die 'View' dar.
Java definiert als Model
die abstrakte Klasse AbstractTableModel,
die das Interface TableModel implementiert,
und davon abgeleitet, die Klasse DefaultTableModel.
Je nach Bedarf können beide Klassen erweitert und
deren Methoden überschrieben werden. In einfachen
Fällen ist dies jedoch nicht notwendig.
Das Beispiel
Es zeigt eine einfache Tabelle mit vier Spalten und vier Reihen, sowie ebenfalls vier Buttons, die die hier besprochenen Löschfunktionen durch Aufruf jeweils einer gesonderten Methode ausführen.
private JTable createTable() {
Object[][] data = new Object[][] {
{ "Ardie", "SV 500", "500", "1930" },
{ "Horex", "Regina Sport", "342", "1952" },
{ "BMW", "R75/5", "750", "1969" },
{ "Triumph", "BDG 250 H", "250", "1952" }
};
Object[] header = new Object[] { "Marke", "Typ", "Hubraum", "Baujahr" };
DefaultTableModel model = new DefaultTableModel(data, header);
JTable table = new JTable(model);
table.setAutoCreateRowSorter(true);
return table;
}
Die Tabelle wird der Übersichtlichkeit halber in
einer gesonderten Methode createTable()
erzeugt. Zunächst wird dort ein zweidimensionales
Array mit String-Daten initialisiert [2-7]. Sie stellen
den Tabelleninhalt dar, wobei die inneren Arrays
für jeweils eine Tabellenzeile stehen. Die
Spaltentitel der Tabelle müssen in einem
gesonderten Array abgelegt werden [8]. Sie dienen zur
Identifizierung der Spalten.
Beide Arrays werden als
Parameter dem Konstruktor bei der Bildung eines DefaultTableModel
übergeben [9]. Der Konstruktor ist sechsfach
überladen; insbesondere können Daten und
Header auch jeweils als Vector
übergeben werden. Das dergestalt erzeugte Model
dient schließlich als Parameter bei der Erzeugung
der JTable [10].
Daten löschen
Wie oben bereits angedeutet muss beim Löschen von
Tabellendaten darauf geachtet werden, dass nicht nur
deren optische Präsenz in der Tabelle entfernt
wird, sondern vielmehr das Löschen auf Ebene des
Models erfolgt. Zur Überprüfung des jeweiligen
Ergebnisses werden hier nach der Löschung alle
Werte des Model auf der Konsole ausgegeben.
Die
Methode printModel() ermittelt hierzu
mittels getRowCount() die Zeilenanzahl des
Models und durch getColumnCount() die Zahl
der Spalten. Sie durchläuft diese innerhalb zweier
verschachtelter Schleifen und gibt die Werte aus. Hierzu
wird die Model-eigene Methode getValueAt()
bei Parametrisierung mit den jeweiligen
Zählvariablen verwendet.
private void printModel(TableModel model) {
for (int i = 0; i < model.getRowCount(); i++) {
for (int j = 0; j < model.getColumnCount(); j++) {
System.out.print(model.getValueAt(i, j) + "\t");
}
System.out.println();
}
}
Löschen von einzelnen Tabellenzellen
Zur Ermittlung eines einzelnen Zelleninhaltes kann durch
die JTable-Methoden getSelectedRow()
und getSelectedColumn() am Schnittpunkt der
selektierten Zeile und Spalte die Zelle ermittelt
werden. Da die Indizes des Modeleintrags bei Erzeugung
mit denjenigen der Tabelle identisch sind, wird dann
durch die Modelmethode setValueAt() der
Wert auf null gesetzt. Durch Verwenden
dieser Methode wird sichergestellt, dass gleichzeitig
alle in Frage kommenden, intern verwendeten
EventListener benachrichtigt werden und somit auch die
Tabellenview entsprechend geändert wird.
Was geschieht jedoch, wenn die Tabelleneinträge
umsortiert wurden, etwa durch Klick auf den Spaltenkopf
und somit View und Model nicht mehr übereinstimmen?
Für diesen Fall wird die Ermittlung der Reihen- und
Spaltenposition über die Methoden convertRowIndexToModel()
bzw. convertColumnIndexToModel()
ausgeführt. Diese, in JTable
definierten Methoden konvertieren die Indices
entsprechend. Beide Methoden haben zudem mit convertRowIndexToView()
und convertColumnIndexToView() auch
Entsprechungen für ein umgekehrtes Vorgehen.
private void deleteCell(JTable table) {
TableModel model = table.getModel();
int y = table.convertRowIndexToModel(table.getSelectedRow());
int x = table.convertColumnIndexToModel(table.getSelectedColumn());
if (x > -1 && y > -1)
model.setValueAt(null, y, x);
printModel(model);
}
Löschen einer Zeile
Für das Löschen einer Zeile stellt DefaultTableModel
die Methode removeRow() bereit. Sie
benötigt den Index der jeweiligen Zeile, der
wiederum durch getSelectedRow() ermittelt
werden kann. Dies funktioniert übrigens auch, wenn
bei der Konfiguration der Tabelle mittels table.setCellSelectionEnabled(true)
statt einer Reihe nur eine einzelne Zelle
ausgewählt wurde.
private void deleteRow(JTable table) {
TableModel model = table.getModel();
int y = table.convertRowIndexToModel(table.getSelectedRow());
((DefaultTableModel) model).removeRow(y);
printModel(model);
}
Löschen einer Spalte
Das Löschen einer Tabellenspalte verhält sich
etwas anders als das bisher Gezeigte. Zwar ist in JTable
eine Methode removeColumn(), der ein TableColumn-Objekt
übergeben wird, deklariert. Sie bewirkt jedoch nur,
dass die entsprechende Spalte aus der Tabellen-View
entfernt wird. Die Daten bleiben im Model erhalten, wie
eine entsprechende Ausgabe mit printModel()
beweist. Sie kann jederzeit, etwa durch Neuladen des
Models, wieder sichtbar gemacht werden.
removeColumn()
erwartet ein TableColumn-Objekt als
Parameter, das nicht direkt über den Index
ermittelt werden kann. Hierzu muss vielmehr ein Umweg
genommen werden: Das benötigte Column-Objekt
wird durch die Methode getColumn()
beschafft. Sie nimmt den Spaltentitel als Parameter
entgegen, der wiederum mittels getColumnName()
über den Index der selektierten Spalte ermittelt
werden kann.
private void deleteCol(JTable table) {
TableModel model = table.getModel();
int x = table.convertColumnIndexToModel(table.getSelectedColumn());
table.removeColumn(table.getColumn(table.getColumnName(x)));
printModel(model);
}
Um eine Spalte auch aus dem Model zu entfernen, bietet
es sich an, ein neues Model durch partielles Kopieren zu
erzeugen und anschließend der Tabelle zu
übergeben. Die Methode deleteColFromModel()
demonstriert hierzu ein mögliches Vorgehen.
Dies geschieht hier dadurch, dass das bisherige Model
durchlaufen wird und die Daten in ein neues kopiert
werden. Die Daten der zu entfernenden Spalte werden
dabei übersprungen.
Hierzu wird für jede
Zeile ein Vector-Objekt, rowVector,
erzeugt [7], in das die Daten kopiert werden [18] und
das dann in der äußeren Schleife dem
Gesamtvector, dataVector, hinzugefügt
wird [20]. Ein weiterer Vector, colIdent,
speichert die verbleibenden Spaltennamen, die beim
Durchlaufen der ersten Zeile durch die Modelmethode getColumnName()
ermittelt werden [16]. Die Variable x, die
den Index der ausgewählten Spalte speichert, wird
bei jedem Durchlauf einer neuen Zeile mit der inneren
Zählvariablen der Spalte verglichen [12]. Sind sie
identisch wird der Kopiervorgang durch continue
unterbrochen [13] und auf diese Weise die
ausgewählte Spalte übersprungen.
Das neue
Model wird schließlich auf die bekannte Art erzeugt
[22] und der Tabelle übergeben [23]. Um die
Änderung der Tabellenstruktur anzustoßen, muss
in einem letzten Schritt die Methode fireTableStructureChanged()
aufgerufen werden [24].
private void deleteColFromModel(JTable table) {
TableModel model = table.getModel();
int rows = model.getRowCount();
int cols = model.getColumnCount();
Vector<Vector<Object>> dataVector = new Vector<Vector<Object>>();
Vector<Object> colIdent = new Vector<Object>();
Vector<Object> rowVector;
int x = table.convertColumnIndexToModel(table.getSelectedColumn());
for (int i = 0; i < rows; i++) {
rowVector = new Vector<Object>();
for (int j = 0; j < cols; j++) {
if (x == j) {
continue;
}
if (i == 0) {
colIdent.add(model.getColumnName(j));
}
rowVector.add(model.getValueAt(i, j));
}
dataVector.add(rowVector);
}
DefaultTableModel newModel = new DefaultTableModel(dataVector, colIdent);
table.setModel(newModel);
newModel.fireTableStructureChanged();
printModel(newModel);
}
Vollständiger Quelltext
package swing.tabelle;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class DeleteTableValues {
public DeleteTableValues() {
JFrame frame = new JFrame();
initGui(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Tabellen-Werte l\u00f6schen");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void initGui(JFrame frame) {
frame.setLayout(new BorderLayout());
JTable table = createTable();
JScrollPane sp = new JScrollPane(table);
sp.setPreferredSize(new Dimension(sp.getPreferredSize().width, 100));
frame.add(sp, BorderLayout.CENTER);
JButton cellDelButt = new JButton("Zelle löschen");
JButton rowDelButt = new JButton("Reihe löschen");
JButton colDelButt = new JButton("Spalte löschen");
JButton modelColDelButt = new JButton("Spalte in Model löschen");
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(1, 3));
buttonPanel.add(cellDelButt);
cellDelButt.addActionListener(e -> deleteCell(table));
buttonPanel.add(rowDelButt);
rowDelButt.addActionListener(e -> deleteRow(table));
buttonPanel.add(colDelButt);
colDelButt.addActionListener(e -> deleteCol(table));
buttonPanel.add(modelColDelButt);
modelColDelButt.addActionListener(e -> deleteColFromModel(table));
frame.add(buttonPanel, BorderLayout.SOUTH);
}
private JTable createTable() {
Object[][] data = new Object[][] { { "Ardie", "SV 500", "500", "1930" },
{ "Horex", "Regina Sport", "342", "1952" }, { "BMW", "R75/5", "750", "1969" },
{ "Triumph", "BDG 250 H", "250", "1952" } };
Object[] header = new Object[] { "Marke", "Typ", "Hubraum", "Baujahr" };
DefaultTableModel model = new DefaultTableModel(data, header);
JTable table = new JTable(model);
table.setAutoCreateRowSorter(true);
return table;
}
private void deleteCell(JTable table) {
TableModel model = table.getModel();
int y = table.convertRowIndexToModel(table.getSelectedRow());
int x = table.convertColumnIndexToModel(table.getSelectedColumn());
if (x > -1 && y > -1)
model.setValueAt(null, y, x);
printModel(model);
}
private void deleteRow(JTable table) {
TableModel model = table.getModel();
int y = table.convertRowIndexToModel(table.getSelectedRow());
((DefaultTableModel) model).removeRow(y);
printModel(model);
}
private void deleteCol(JTable table) {
TableModel model = table.getModel();
int x = table.convertColumnIndexToModel(table.getSelectedColumn());
table.removeColumn(table.getColumn(table.getColumnName(x)));
printModel(model);
}
private void deleteColFromModel(JTable table) {
TableModel model = table.getModel();
int rows = model.getRowCount();
int cols = model.getColumnCount();
Vector<Vector<Object>> dataVector = new Vector<Vector<Object>>();
Vector<Object> colIdent = new Vector<Object>();
Vector<Object> rowVector;
int x = table.convertColumnIndexToModel(table.getSelectedColumn());
for (int i = 0; i < rows; i++) {
rowVector = new Vector<Object>();
for (int j = 0; j < cols; j++) {
if (x == j) {
continue;
}
if (i == 0) {
colIdent.add(model.getColumnName(j));
}
rowVector.add(model.getValueAt(i, j));
}
dataVector.add(rowVector);
}
DefaultTableModel newModel = new DefaultTableModel(dataVector, colIdent);
table.setModel(newModel);
newModel.fireTableStructureChanged();
printModel(newModel);
}
private void printModel(TableModel model) {
for (int i = 0; i < model.getRowCount(); i++) {
for (int j = 0; j < model.getColumnCount(); j++) {
System.out.print(model.getValueAt(i, j) + "\t");
}
System.out.println();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new DeleteTableValues());
}
}
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.