当标签需要很长时间加载时,向JTabbedPane添加选项卡会导致线程AWT-EventQueue-0中出现ArrayIndexOutOfBoundsException

时间:2018-04-26 12:44:56

标签: java swing indexoutofboundsexception jtabbedpane

我有以下SSCCE,需要JTabbedPane,并添加500个包含CustomJPanel的标签。请注意,组件CustomJPanel是" long"生成,因为我(故意)向其添加100 JLabel

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

public class Class1 {
    public static void main(String[] args) {

        JFrame window = new JFrame();
        window.setTitle("Parent frame");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setSize(1200, 800);
        window.setLocationRelativeTo(null);

        window.setVisible(true);

        JTabbedPane jtp = new JTabbedPane();
        window.add(jtp);

        //new Thread(new Runnable() {
        //  public void run() {
                for (int i = 0; i < 500; i++) {

                    jtp.addTab("tab"+i, new CustomJPanel());
                }
        //  }
        //}).start();

    }
}

class CustomJPanel extends JPanel {

    public CustomJPanel() {
        for (int i = 0; i < 100; i++) {
            this.add(new JLabel("test"));
        }
    }

}

当我运行此代码时,我很有可能获得以下异常:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 0
    at javax.swing.plaf.basic.BasicTabbedPaneUI.tabForCoordinate(Unknown Source)
    at javax.swing.plaf.basic.BasicTabbedPaneUI.setRolloverTab(Unknown Source)
    at javax.swing.plaf.basic.BasicTabbedPaneUI.access$2100(Unknown Source)
    at javax.swing.plaf.basic.BasicTabbedPaneUI$Handler.mouseEntered(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEnterExit(Unknown Source)
    at java.awt.LightweightDispatcher.trackMouseEnterExit(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$500(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

当我在JTabbedPane中快速添加需要很长时间才能生成的组件时,会导致此问题。如果我删除CustomJPanel中的for循环,使其只保留一个JLabel,则不会发生异常。请注意,虽然在此示例中,异常始终为&#34; 0&#34;,但在我的应用中,它可以是任意数字。

如果在单独的线程中添加选项卡,则异常似乎发生的可能性更高。

我发现降低获得此异常的可能性的唯一方法是在添加每个选项卡之间添加几十毫秒的延迟(Thread.Sleep())。但是,它仍然会不时发生。

有没有办法阻止此异常发生,或阻止它在控制台中显示?请注意,我无法抓住它,因为它并没有指向我的代码中的任何内容,它是所有&#34;未知来源&#34;。

修改:我已将Class1的代码更改为在EDT上运行:

public class Class1 {
    public static JFrame window;
    public static JTabbedPane jtp;
    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                window = new JFrame();
                window.setTitle("Parent frame");
                window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                window.setSize(1200, 800);
                window.setLocationRelativeTo(null);

                window.setVisible(true);

                jtp = new JTabbedPane();
                window.add(jtp);

                new SwingWorker<Void, Void>() {
                    @Override
                    public Void doInBackground() {
                        for (int i = 0; i < 500; i++) {
                            jtp.addTab("tab"+i, new CustomJPanel());

                        }
                        return null;
                    }

                    @Override
                    public void done() {}

                }.execute();
            }
        });
    }
}

然而,异常仍然发生。我有什么问题吗?

1 个答案:

答案 0 :(得分:1)

您错误地使用了doInBackground()。当你在背景中&#34;&#34;你不在美国东部时间。因此,调用UI的addTab()调用是禁止的。

要正确使用SwingWorker,您必须publish()中间结果。 process()方法在EDT上下文中接收已发布的结果,可以安全地修改UI。

public class Class1 {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(Class1::new);
    }

    Class1() {
        JFrame window = new JFrame();
        window.setTitle("Parent frame");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setSize(1200, 800);
        window.setLocationRelativeTo(null);

        JTabbedPane jtp = new JTabbedPane();
        window.add(jtp);

        window.setVisible(true);

        new Builder(jtp).execute();
    }
}

class Builder extends SwingWorker<Void, CustomJPanel> {
    JTabbedPane jtp;

    Builder(JTabbedPane _jtp) {
        jtp = _jtp;
    }

    @Override
    protected Void doInBackground() throws Exception {
        for (int i = 0; i < 500; i++) {
            CustomJPanel panel = new CustomJPanel();
            publish(panel);
        }
        return null;
    }

    @Override
    protected void process(List<CustomJPanel> panels) {
        int i = jtp.getTabCount();
        for (CustomJPanel panel : panels) {
            jtp.addTab("tab" + i, panel);
            i++;
        }
    }
}

注意:创建CustomJPanel extends JPanel可能看起来像是在操纵UI,但实际上并非如此。 JPanel尚未实现。只有将JPanel添加到已实现的UI项目(例如JTabbedPane)时,才能真正实现。因此,我们实际上可以安全地创建JPanel,并在工作线程中使用JLabel个孩子等。但它无法添加到工作线程中已实现的UI项目中。