You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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;
    }
}

###测试验证
修改后按照你的步骤再试一遍:

  1. 点击Fill按钮填充表格
  2. 双击第一行的Name单元格进入编辑状态
  3. 点击Remove row按钮

这次编辑器会先自动关闭,然后第一行被正常删除,再也不会出现编辑器跳转到下一行的诡异情况了!


额外补充

如果你有批量删除、快捷键删除等其他删除场景,记得也要加上这个“先结束编辑”的判断;另外,也可以给JTable全局设置自动结束编辑的逻辑,比如在表格失去焦点时自动停止编辑,不过针对这个特定场景,在删除前手动处理是最直接有效的方案。

火山引擎 最新活动