D) JTable – Momentan editierte Zelle abfragen
Vielleicht kennen Sie das Problem? Sie haben eine editierbare JTable
eingebunden, und möchten die eingegebenen Werte bspw. nach einem Klick auf einen JButton
abfragen, verarbeiten oder speichern. Leider hat der User zuvor noch eine Zelle editiert, so dass diese beim Klick auf den JButton
noch aktiv war. Dies hat zur Folge, dass in der momentan editierte Zelle beim Auslesen noch der alte Wert, nicht aber die Benutzereingabe steht. In diesem Kapitel zeigen wir Ihnen die Lösung dieses Problems.
Das Problem
Zum besseren Verständnis bauen Sie zuerst das Problem nach. Kompilieren und führen Sie hierzu diesen Code aus:
package de.jbb.table; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; public class JTableProblem { private static JFrame frame; private static JTable table; private static JButton readValues; public static void main(String[] args) { JTableProblem.frame = new JFrame(); JTableProblem.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JTableProblem.frame.setLayout(new BorderLayout()); String[][] content = new String[][] { {"Value 1/1", "Value 1/2", "Value 1/3"}, {"Value 2/1", "Value 2/2", "Value 2/3"}, {"Value 3/1", "Value 3/2", "Value 3/3"} }; String[] head = new String[] {"Column1", "Column2", "Column3"}; JTableProblem.table = new JTable(content, head); JTableProblem.readValues = new JButton("Read"); JTableProblem.readValues.addActionListener(JTableProblem.al); JTableProblem.frame.add(new JScrollPane(JTableProblem.table)); JTableProblem.frame.add(JTableProblem.readValues, BorderLayout.SOUTH); JTableProblem.frame.pack(); JTableProblem.frame.setVisible(true); } private static ActionListener al = new ActionListener() { public void actionPerformed(ActionEvent arg0) { for (int y = 0; y < JTableProblem.table.getRowCount(); y++) { for (int x = 0; x < JTableProblem.table.getColumnCount(); x++) { System.out.println("Value " + (x + 1) + "/" + (y + 1) + "=" + JTableProblem.table.getValueAt(x, y)); } } } }; }[/sourcecode] Es wird eine Tabelle mit editierbaren Zellen erzeugt. Wenn Sie auf <em>Read</em> klicken, werden die Zelleninhalt ausgelesen und untereinander aufgelistet. Dies funktioniert soweit ohne Probleme. Überschreiben Sie jetzt die erste Spalte der ersten Zeile mit einem anderen Wert und klicken Sie anschließend <strong>sofort</strong> auf <em>Read</em> (eben so, wie es ein User Ihrer Anwendung auch machen würde). Sie werden feststellen, dass dieser neue Wert nicht übernommen wurde, und dass stattdessen der alte Wert ausgegeben wird. <strong>Die Lösung</strong> Der neue Wert wird erst dann übernommen, wenn der User vorher das Editieren der Zelle beenden würde, indem er z. B. in eine andere Zelle klickt. Dies können Sie aber selbstverständlich nicht von Ihren Usern verlangen! Deshalb benötigen Sie eine Lösung auf Programmiererebene, die beim Speichern oder Verlassen der Tabelle das Editieren beendet. Hierzu bieten sich zwei Möglichkeiten an: <strong>Die Universal-Lösung - Editieren beim Verlassen der JTable beenden</strong> Mit dieser Lösung fangen Sie das Problem an der Wurzel ab. Sobald der User die <code>JTable</code> verlässt, die <code>JTable</code> also den Fokus verliert, wird das Editieren der aktuellen Zelle abgebrochen und der Inhalt "festgeschrieben", so dass er später durch Sie abfragbar ist. Hierzu müssen Sie eine Property der Tabelle verändern. Fügen Sie folgende Zeile bei der Initialisierung Ihrer <code>JTable</code> ein: [sourcecode language="java"]JTableProblem.table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
Beachten Sie aber, dass diese Property nicht von jeder Runtime Environment unterstützt werden muss!
Editieren der Zelle beim Button-Klick abbrechen
Eine aufwändigere, da vor jedem Auslesen anzuwendende, aber sicherere Methode ist es, den Editor der JTable
anzuweisen, bevor die Daten ausgelesen werden, das Editieren abzubrechen. Fügen Sie folgende Zeile vor dem Auslesen des Inhalts ein (in unserem Fall im ActionListener
vor der äußeren for
-Schleife):
JTableProblem.table.getDefaultEditor(Object.class).stopCellEditing();
Ersetzen Sie hierbei ggf. Object.class
durch die Klasse, die der Editor bearbeiten soll.
Hallo Java-Blog-Buch Autoren,
der Tipp ist einfach nur klasse und findet bei meinen Programmen nun häufige Verwendung.
Viele Grüße
Marcus
Könnte man das ganze nicht auch mit einem normalen FocusListener machen?
Also
JTableProblem.table.addFocusListener(this);
und dann müsste er wenn man auf die Ausgabe klickt den Fokus ja automatisch verlieren sodass man erst noch den neuen wert bekommt.
Hallo Marcel,
nein, so einfach geht das leider nicht, da nicht die JTable den Focus hat, sondern die jeweilige Zelle. Am Ende bläht man den Code mit der FocusListener-Variante nur unnötig auf.
Grüße
Stefan
Genial Einfach!
Hallo zusammen,
nach zwei Jahren noch ein Bemerkung:
folgender Code geht auch:
if (table.getCellEditor() != null) {
table.getCellEditor().cancelCellEditing();
}
und hat den gleichen Effekt, wie Eure Lösung.
Grüße
Marcus