首页 > 解决方案 > 从同一列的组件菜单中删除 JTable 列时出错

问题描述

我有一个JTable为表头设置的组件菜单。它包含用于删除列的条目。删除触发组件菜单的同一列时会出现我的问题,并且似乎将此操作视为正在拖动的列,这会导致ArrayIndexOutOfBoundsExceptionwith -1

如何安全地从组件菜单中删除当前列而不发生此错误?

这是一个如何触发这种行为的最小示例。只需运行它并删除您所在的同一列(请注意,当您删除最后一列时它似乎可以工作,但这对我来说几乎不相关):

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

public class BasicExample extends JFrame {

    private JTable t;
    private DefaultTableModel dtm;

    public BasicExample() {
        init();
    }

    private void init() {
        dtm = new DefaultTableModel(new String[][]{{"a", "b"}, {"c", "d"}}, new String[]{"A", "B"});
        t = new JTable(dtm);
        t.getTableHeader().setComponentPopupMenu(new PopupMenu(t));
        this.setLayout(new BorderLayout());
        add(t.getTableHeader(), BorderLayout.NORTH);
        add(t, BorderLayout.CENTER);
        pack();
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public class PopupMenu extends JPopupMenu {

        private JTable table;

        public PopupMenu(JTable table) {
            this.table = table;
            init();
        }

        private void init() {
            for (int i = 0; i < table.getModel().getColumnCount(); i++) {
                String columnName = table.getModel().getColumnName(i);

                JMenuItem item = new JMenuItem(columnName);
                item.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        TableColumn tc = table.getColumn(columnName);
                        table.getColumnModel().removeColumn(tc);
                    }
                });
                this.add(item);
            }
        }
    }

    public static void main(String[] args) {
        BasicExample be = new BasicExample();
    }
}

在堆栈跟踪中,我看到它似乎将该列视为已拖动,因为它输入了条件if (draggedColumn != null) {in BasicTableHeaderUI.java,而我正在执行的操作并不是真正的拖动。完整的堆栈跟踪如下:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
    at java.util.Vector.elementData(Vector.java:734)
    at java.util.Vector.elementAt(Vector.java:477)
    at javax.swing.table.DefaultTableColumnModel.getColumn(DefaultTableColumnModel.java:294)
    at javax.swing.plaf.basic.BasicTableHeaderUI.getHeaderRenderer(BasicTableHeaderUI.java:693)
    at javax.swing.plaf.basic.BasicTableHeaderUI.paintCell(BasicTableHeaderUI.java:709)
    at javax.swing.plaf.basic.BasicTableHeaderUI.paint(BasicTableHeaderUI.java:685)
    at javax.swing.plaf.ComponentUI.update(ComponentUI.java:161)
    at javax.swing.JComponent.paintComponent(JComponent.java:780)
    at javax.swing.JComponent.paint(JComponent.java:1056)
    at javax.swing.JComponent.paintToOffscreen(JComponent.java:5210)
    at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1579)
    at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1502)
    at javax.swing.RepaintManager.paint(RepaintManager.java:1272)
    at javax.swing.JComponent._paintImmediately(JComponent.java:5158)
    at javax.swing.JComponent.paintImmediately(JComponent.java:4969)
    at javax.swing.RepaintManager$4.run(RepaintManager.java:831)
    at javax.swing.RepaintManager$4.run(RepaintManager.java:814)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:814)
    at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:789)
    at javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:738)
    at javax.swing.RepaintManager.access$1200(RepaintManager.java:64)
    at javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1732)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

如果相关,这发生在 Java 8 中。更改 JRE/JDK 不是一种选择。

标签: javaswingjtableindexoutofboundsexceptionjpopupmenu

解决方案


一种似乎可行的方法是取消设置菜单项操作中的拖动列:

@Override
public void actionPerformed(ActionEvent e) {
    table.getTableHeader().setDraggedColumn(null);
    TableColumn tc = table.getColumn(columnName);
    table.getColumnModel().removeColumn(tc);
}

需要注意的是,如果使用右键单击拖动表格,并且未选择任何操作或该操作不会导致重新绘制表格,这可能会阻止重新绘制表格。


推荐阅读