Swing JTable删除编辑中行时编辑器异常跳转的问题及解决方案咨询
Swing JTable删除编辑中行时编辑器异常跳转的问题及解决方案咨询
嘿,这个问题我之前也踩过同款坑!确实是Swing JTable的一个经典小bug——当你正在编辑某行单元格时直接删除该行,编辑器组件不会自动关闭,反而会“粘”到下一行,看起来特别离谱。本质原因很简单:JTable在删除行之前没有先结束当前的编辑会话,导致编辑器的视图还挂在UI上,表格行索引变化后,编辑器就错位到下一行的位置了。
问题根源
默认情况下,点击删除按钮的操作和JTable的编辑状态是完全独立的。当你调用model.removeRow(selectedRow)时,表格的数据集被修改,但活跃的单元格编辑器并没有被通知要销毁或隐藏,它的视图会跟着表格的行偏移,最终显示在错误的位置上。
最直接的修复方案
在删除行的逻辑执行前,先强制结束JTable的当前编辑状态。只需要修改removeSelectedRow()方法,添加几行检查编辑状态并结束编辑的代码即可:
private static void removeSelectedRow() { // 核心修复:先结束当前的编辑会话 if (table.isEditing()) { // 如果你想保留编辑的内容,用stopCellEditing() table.getCellEditor().stopCellEditing(); // 如果你想丢弃编辑的内容,改用cancelCellEditing(): // table.getCellEditor().cancelCellEditing(); } DefaultTableModel model = (DefaultTableModel) table.getModel(); int selectedRow = table.getSelectedRow(); model.removeRow(selectedRow); }
修复后的完整可运行代码
这里把所有代码整合好,方便你直接测试:
import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.WindowConstants; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Container; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SimpleTableDemo { static JTable table; static JButton fillButton; static JButton removeRowButton; public static void main(String[] args) { Container mainPanel = createMainPanel(); JFrame frame = new JFrame("Table Demo"); frame.setContentPane(mainPanel); frame.setLocationRelativeTo(null); frame.pack(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setVisible(true); } private static Container createMainPanel() { JPanel mainPanel = new JPanel(new BorderLayout()); mainPanel.add(createScroller()); mainPanel.add(createButtonPanel(), BorderLayout.SOUTH); return mainPanel; } private static Component createScroller() { JScrollPane scroller = new JScrollPane(); scroller.setViewportView(createTable()); return scroller; } private static Component createTable() { table = new JTable() { @Override public void addColumn(TableColumn aColumn) { aColumn.setModelIndex(getColumnCount()); super.addColumn(aColumn); } }; table.setAutoCreateColumnsFromModel(false); table.getSelectionModel().addListSelectionListener(e -> removeRowButton.setEnabled(table.getSelectedRows().length != 0)); List<TableColumn> columns = Arrays.asList(TableColumns.code(), TableColumns.name()); columns.forEach(table::addColumn); DefaultTableModel model = (DefaultTableModel) table.getModel(); model.setColumnCount(table.getColumnCount()); model.setColumnIdentifiers(columns.stream().map(TableColumn::getIdentifier).toArray()); return table; } private static Component createButtonPanel() { JPanel panel = new JPanel(); panel.add(createFillButton()); panel.add(createRemoveRowButton()); return panel; } private static Component createFillButton() { fillButton = new JButton("Fill"); fillButton.addActionListener(e -> fillTable()); return fillButton; } private static void fillTable() { List<Doctor> doctors = createDoctors(); DefaultTableModel model = (DefaultTableModel) table.getModel(); model.setRowCount(0); for (Doctor doctor : doctors) { String code = doctor.getCode(); String name = doctor.getName(); model.addRow(new Object[]{code, name}); } } private static Component createRemoveRowButton() { removeRowButton = new JButton("Remove row"); removeRowButton.setEnabled(false); removeRowButton.addActionListener(e -> removeSelectedRow()); return removeRowButton; } // 修复后的删除方法 private static void removeSelectedRow() { // 先结束当前的编辑会话,避免编辑器错位 if (table.isEditing()) { table.getCellEditor().stopCellEditing(); // 若需丢弃编辑内容,替换为 table.getCellEditor().cancelCellEditing(); } DefaultTableModel model = (DefaultTableModel) table.getModel(); int selectedRow = table.getSelectedRow(); model.removeRow(selectedRow); } private static List<Doctor> createDoctors() { List<Doctor> doctors = new ArrayList<>(); doctors.add(Doctor.of("1", "Maria")); doctors.add(Doctor.of("2", "Patrick")); return doctors; } } class Doctor { private String code; private String name; private Doctor() { } public static Doctor of(String code, String name) { Doctor doctor = new Doctor(); doctor.code = code; doctor.name = name; return doctor; } public String getName() { return name; } public String getCode() { return code; } } class TableColumns { private TableColumns() { } public static TableColumn code() { TableColumn column = new TableColumn(); column.setIdentifier("CODE"); column.setHeaderValue("Code"); return column; } public static TableColumn name() { TableColumn column = new TableColumn(); column.setIdentifier("NAME"); column.setHeaderValue("Name"); return column; } }
###测试验证
修改后按照你的步骤再试一遍:
- 点击Fill按钮填充表格
- 双击第一行的Name单元格进入编辑状态
- 点击Remove row按钮
这次编辑器会先自动关闭,然后第一行被正常删除,再也不会出现编辑器跳转到下一行的诡异情况了!
额外补充
如果你有批量删除、快捷键删除等其他删除场景,记得也要加上这个“先结束编辑”的判断;另外,也可以给JTable全局设置自动结束编辑的逻辑,比如在表格失去焦点时自动停止编辑,不过针对这个特定场景,在删除前手动处理是最直接有效的方案。




