Java 从同一列的组件菜单中删除JTable列时出错

Java 从同一列的组件菜单中删除JTable列时出错,java,swing,jtable,indexoutofboundsexception,jpopupmenu,Java,Swing,Jtable,Indexoutofboundsexception,Jpopupmenu,我有一个JTable,为表格标题设置了一个组件菜单。它包含用于删除列的条目。我的问题发生在删除触发组件菜单的同一列时,它似乎将此操作视为正在拖动的列,从而导致出现ArrayIndexOutOfBoundsException和-1 如何安全地从组件菜单中删除当前列而不产生此错误? 下面是一个如何触发这种行为的最小示例。只需运行它并删除您所在的同一列(请注意,当您删除最后一列时,它似乎起作用,但这与我无关): 如果相关,则在Java8中会出现这种情况。更改JRE/JDK不是一个选项。我们的环境中偶然

我有一个
JTable
,为表格标题设置了一个组件菜单。它包含用于删除列的条目。我的问题发生在删除触发组件菜单的同一列时,它似乎将此操作视为正在拖动的列,从而导致出现
ArrayIndexOutOfBoundsException
-1

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

下面是一个如何触发这种行为的最小示例。只需运行它并删除您所在的同一列(请注意,当您删除最后一列时,它似乎起作用,但这与我无关):


如果相关,则在Java8中会出现这种情况。更改JRE/JDK不是一个选项。

我们的环境中偶然出现了相同的错误,但实际上是偶尔出现的,因此我无法重现它。 但是你的例子是完美的,我能够重现我的错误。请参阅下面的代码

我在调查期间修改了我的答案。旧注释不反映最新代码。

您的是一个报告的错误:

下面是一个基于bug解决方案的解决方案:

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

            t.getTableHeader().setDraggedColumn(null);

            table.getColumnModel().removeColumn(tc);
          }
        });
        this.add(item);
      }
    }
  }

  public static void main(String[] args) {
    BasicExample be = new BasicExample();
  }
}
导入java.awt.BorderLayout;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入javax.swing.JFrame;
导入javax.swing.JMenuItem;
导入javax.swing.jpopmenu;
导入javax.swing.JTable;
导入javax.swing.table.DefaultTableModel;
导入javax.swing.table.TableColumn;
公共类BasicExample扩展了JFrame{
专用JTT表;
私有模型dtm;
公共基础示例(){
init();
}
私有void init(){
dtm=新的DefaultTableModel(新字符串[][{{“a”,“b”},{“c”,“d”}),
新字符串[]{“A”,“B”});
t=新JTable(dtm);
t、 getTableHeader().setComponentPopupMenu(新的PopupMenu(t));
此.setLayout(新的BorderLayout());
添加(t.getTableHeader(),BorderLayout.NORTH);
添加(t,BorderLayout.CENTER);
包装();
setVisible(真);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
公共类弹出菜单扩展了JPOppMenu{
专用JTable表;
公共弹出菜单(JTable表格){
this.table=表格;
init();
}
私有void init(){
对于(int i=0;i
只需在删除之前将draggedColumn设置为null

我一直在挖掘,因为它没有解释为什么在我们的环境中,bug很少发生,而且只发生在QA中。:)

下面是代码在我们的环境中的外观(再次感谢非常好的小示例)

导入java.awt.BorderLayout;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.MouseAdapter;
导入java.awt.event.MouseEvent;
导入javax.swing.JFrame;
导入javax.swing.JMenuItem;
导入javax.swing.jpopmenu;
导入javax.swing.JTable;
导入javax.swing.table.DefaultTableModel;
导入javax.swing.table.TableColumn;
公共类BasicExample扩展了JFrame{
专用JTT表;
私有模型dtm;
公共基础示例(){
init();
}
私有void init(){
dtm=新的DefaultTableModel(新字符串[][{{“a”,“b”},{“c”,“d”}),
新字符串[]{“A”,“B”});
t=新JTable(dtm);
弹出菜单lPopupMenu=新弹出菜单(t);
//t.getTableHeader().setComponentPopupMenu(新的PopupMenu(t));
t、 getTableHeader().addMouseListener(新的MouseAdapter()){
@凌驾
公共无效鼠标按下(MouseEvent pE){
lpopumenu.show(pE.getComponent(),pE.getX(),pE.getY());
}
});
此.setLayout(新的BorderLayout());
添加(t.getTableHeader(),BorderLayout.NORTH);
添加(t,BorderLayout.CENTER);
包装();
setVisible(真);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
公共类弹出菜单扩展了JPOppMenu{
专用JTable表;
公共弹出菜单(JTable表格){
this.table=表格;
init();
}
私有void init(){
对于(int i=0;i
代码的不同之处在于我们不使用setComponentPopupMenu(),而是手动显示PopupMenu。您的用例不会对我们的代码产生异常。
为什么?
好问题,我没有答案。
不知何故,您的代码jpopmenu会消耗鼠标事件。使用我们的代码jpopmpmenu将鼠标事件传播到底层组件。如果将UI替换为表头,则可以看到它。
这就是我用我们的代码复制异常的方式:在标题上按鼠标右键,但不要释放它,并保持鼠标在弹出窗口上移动,远离标题。当指针离开标题时,释放右按钮(使标题不移动)
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)
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);

            t.getTableHeader().setDraggedColumn(null);

            table.getColumnModel().removeColumn(tc);
          }
        });
        this.add(item);
      }
    }
  }

  public static void main(String[] args) {
    BasicExample be = new BasicExample();
  }
}
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

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

    PopupMenu lPopupMenu = new PopupMenu(t);

    //    t.getTableHeader().setComponentPopupMenu(new PopupMenu(t));
    t.getTableHeader().addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent pE) {
        lPopupMenu.show(pE.getComponent(), pE.getX(), pE.getY());
      }
    });

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

            //            t.getTableHeader().setDraggedColumn(null);

            table.getColumnModel().removeColumn(tc);
          }
        });
        this.add(item);
      }
    }
  }

  public static void main(String[] args) {
    BasicExample be = new BasicExample();
  }
}
@Override
public void actionPerformed(ActionEvent e) {
    table.getTableHeader().setDraggedColumn(null);
    TableColumn tc = table.getColumn(columnName);
    table.getColumnModel().removeColumn(tc);
}