java - Adding tabs to JTabbedPane causes ArrayIndexOutOfBoundsException in thread AWT-EventQueue-0 when tabs take a long time to load
问题描述
我有以下 SSCCE,它需要一个JTabbedPane
,并向其中添加 500 个包含 a 的选项卡CustomJPanel
。请注意,组件 ,CustomJPanel
是“长”生成的,因为我(故意)JLabel
向它添加 100 秒。
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
需要很长时间才能生成的组件中快速添加组件时会导致问题。如果我删除 for 循环以CustomJPanel
使其仅包含 one JLabel
,则不会发生异常。请注意,尽管在此示例中异常始终为“0”,但在我的应用程序中它可以是任何数字。
如果在单独的线程中添加选项卡,则似乎发生异常的可能性更高。
我发现减少出现此异常的可能性的唯一方法是Thread.Sleep()
在添加每个选项卡之间添加几十毫秒的延迟(使用 )。但是,它仍然不时发生。
有什么办法可以防止这种异常的发生,或者防止它在控制台中显示?请注意,我无法捕捉到它,因为它没有指向我的代码中的任何内容,它都是“未知来源”。
编辑:我已经更改了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();
}
});
}
}
但是,异常仍然发生。有什么我做错了吗?
解决方案
您使用doInBackground()
不正确。当您在“后台”时,您不在 EDT 上。addTab()
因此,修改 UI 的调用是禁止的。
要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
带有子等的 , complete 。JLabel
但它不能添加到工作线程中实现的 UI 项中。
推荐阅读
- python - 我不明白这个函数是如何工作的,以及它是如何得出 60 的结果的
- python - 在 Python 中的概率模型上添加常量
- json - 可以在 TSQL 中使用 JSON AUTO 来创建有孩子的兄弟姐妹吗?
- c# - 解析 SQL 插入语句并获取 N'VALUE' 之间的所有字符串值
- javascript - Vue 未使用来自 Axios 请求的数据更新数组中的 Vuetify 数据表
- javascript - LeetCode: 1281. 减去一个整数的乘积和位数之和
- c++ - 链接规则是否像全局名称一样适用于命名空间?
- perl - 设置不活动超时 Perl (Mojo) 子进程
- algorithm - 将二叉树转换为有序键数组的最有效方法是什么?
- java - 发布到 Artifactory 时如何包含阴影?(毕业)