在JTable中如何触发DefaultCellEditor.EditorDelegate的startCellEditing方法?
问题根源与修正方案
1. 为什么JTextField的MouseListener没触发?
JTable的单元格在未进入编辑状态时,显示的是单元格渲染器(TableCellRenderer),而非你定义的JTextField编辑器组件。只有当编辑真正启动后,编辑器组件才会被挂载到JTable的界面层级中并显示。因此你双击单元格时,点击的是渲染器组件,不是tf,自然触发不了tf上的MouseListener。
2. 为什么最初的startCellEditing方法没触发?
实际上这个方法会被触发,但你的代码存在两个问题:
- 你只给第1列(Count列)绑定了自定义编辑器,只有双击该列的单元格才会触发这个方法,双击其他列不会有反应。
- 你通过
table.getSelectedRow()获取行号的方式不可靠:当通过键盘(比如按F2)触发编辑时,选中的行/列可能和编辑的单元格不一致,应该改用JTable的getEditingRow()和getEditingColumn()方法获取当前编辑的单元格位置。
修正后的代码
移除无用的MouseListener,修正startCellEditing中的单元格位置获取方式,同时去掉多余的文本设置逻辑(DefaultCellEditor会自动同步单元格值到编辑器):
import java.awt.*; import java.awt.event.*; import java.util.EventObject; import javax.swing.*; import javax.swing.table.*; public class MonitorEditingStartStopped extends JFrame { public MonitorEditingStartStopped() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(220, 150); setLocationRelativeTo(null); String headers[] = {"Fruit", "Count", "Profit"}; Object data[][] = { {"Apple", 6, -0.3}, {"Banana", 3, 0.4}, {"Cherry", 10, 1.}, {"Date", -3, -1.} }; DefaultTableModel model= new DefaultTableModel(headers, 0) { public Class<?> getColumnClass(int column) { Class<?> returnValue; if (getRowCount()>0 && getValueAt(0, column)!=null) returnValue= getValueAt(0, column).getClass(); else returnValue= Object.class; return returnValue; } }; for (int i=0; i<data.length; i++) { model.addRow(data[i]); } JTable table= new JTable(model); JTextField tf= new JTextField(10); tf.setHorizontalAlignment(SwingConstants.RIGHT); StartStopEditor editor= new StartStopEditor(table, tf); TableColumnModel tcm= table.getColumnModel(); tcm.getColumn(1).setCellEditor(editor); JScrollPane scrollPane= new JScrollPane(table); add(scrollPane, BorderLayout.CENTER); setVisible(true); } public static void main(String args[]) { EventQueue.invokeLater(MonitorEditingStartStopped::new); } class StartStopEditor extends DefaultCellEditor { private final JTable table; public StartStopEditor(JTable table, JTextField tf) { super(tf); this.table = table; delegate= new EditorDelegate() { @Override public Object getCellEditorValue() { if (tf.getText().isBlank()) return Integer.valueOf(0); return Integer.valueOf(tf.getText()); } @Override public boolean startCellEditing(EventObject anEvent) { // 使用JTable的getEditingRow/Column获取当前编辑的单元格位置 int row= table.getEditingRow(); int column= table.getEditingColumn(); System.out.printf("started %d, %d%n", row, column); // 移除手动设置文本的逻辑,DefaultCellEditor会自动同步值到tf return super.startCellEditing(anEvent); } @Override public boolean stopCellEditing() { System.out.printf("editing stopped %d%n", getCellEditorValue()); return super.stopCellEditing(); } }; } } }
额外说明
如果你想监听所有单元格的编辑开始事件,除了重写prepareEditor,也可以通过给JTable添加CellEditorListener实现:
table.getDefaultEditor(Object.class).addCellEditorListener(new CellEditorListener() { @Override public void editingStopped(ChangeEvent e) { // 编辑结束逻辑 } @Override public void editingCanceled(ChangeEvent e) { // 编辑取消逻辑 } });
不过针对特定列的编辑器,你写的自定义EditorDelegate方式更直接。
内容的提问来源于stack exchange,提问作者Jörg
相关产品推荐
相关产品推荐

