Java 调用getPreferredSize()的仅EDT代码中的BoxLayout异常

Java 调用getPreferredSize()的仅EDT代码中的BoxLayout异常,java,swing,Java,Swing,以下是: public class TestCode { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { System.err.println("Showing popup"); // Create a popup menu

以下是:

public class TestCode {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                System.err.println("Showing popup");
                // Create a popup menu and fill it with some component
                JPopupMenu basicPopupMenu = new JPopupMenu();
                JTextArea textArea = new JTextArea();
                textArea.setLineWrap(true);
                basicPopupMenu.add(textArea);
                // This is not necessary for the crash, just to verify the pref size is not null at this point
                System.out.println("Popup pref size is : " + basicPopupMenu.getPreferredSize());
                System.out.println("Text area pref size is : " + textArea.getPreferredSize());
                basicPopupMenu.show(null, 0, 0);
                // This is not necessary for the crash, just to verify the minimum size is not null at this point
                System.out.println("Min size is : " + basicPopupMenu.getMinimumSize());
                System.out.println("Text area min size is : " + textArea.getMinimumSize());
                // If I uncomment the next line, then the crash does not occur
                // textArea.getPreferredSize()
                try {
                    basicPopupMenu.getPreferredSize();
                }
                catch (Exception e) {
                    System.err.println("getPreferredSize() exception!");
                    e.printStackTrace();
                }
            }
        });
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Test finished");
    }
}
系统性崩溃,包括:

Showing popup
Popup pref size is : java.awt.Dimension[width=102,height=21]
Text area pref size is : java.awt.Dimension[width=100,height=16]
Min size is : java.awt.Dimension[width=102,height=21]
Text area min size is : java.awt.Dimension[width=100,height=16]
getPreferredSize() exception!
java.lang.NullPointerException
    at javax.swing.BoxLayout.checkRequests(BoxLayout.java:466)
    at javax.swing.BoxLayout.preferredLayoutSize(BoxLayout.java:281)
    at javax.swing.plaf.basic.DefaultMenuLayout.preferredLayoutSize(DefaultMenuLayout.java:43)
    at java.awt.Container.preferredSize(Container.java:1597)
    at java.awt.Container.getPreferredSize(Container.java:1582)
    at javax.swing.JComponent.getPreferredSize(JComponent.java:1636)
    at TestPopup$1.run(TestPopup.java:26)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:646)
    at java.awt.EventQueue.access$000(EventQueue.java:84)
    at java.awt.EventQueue$1.run(EventQueue.java:607)
    at java.awt.EventQueue$1.run(EventQueue.java:605)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:616)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Test finished

很难看出这在哪里误用了swing API,因为它应该完全在EDT中运行,并且首选大小应该在崩溃之前设置,事实上,在上面的堆栈中似乎没有一点最小/首选大小设置为null。这里是否可能调用了另一个线程,或者布局中缺少了什么?在我的Java环境中,唯一具有此跟踪的标记为fixed。

这似乎是的一个功能,请参阅。它扩展了
BoxLayout
,并需要一个非空的目标
容器
,而
BasicPopupMenuUI
将其延迟。一种选择是使用不同的布局,例如:

basicPopupMenu.setLayout(new GridLayout());

这似乎是一个特征,可见。它扩展了
BoxLayout
,并需要一个非空的目标
容器
,而
BasicPopupMenuUI
将其延迟。一种选择是使用不同的布局,例如:

basicPopupMenu.setLayout(new GridLayout());
  • 出现这种情况的原因是show方法调整JPOppMenu的JTextArea子组件的宽度(在本例中,因为组件具有setLineWrap(true),show()将宽度从0更改为100)。在此状态下,JTextArea上对getPreferredSize()的第一次调用将更新其父组件的大小,为了导致崩溃,此第一次调用必须发生在JPopupMenu getPreferredSize()方法内部
  • 此崩溃仅在JPOppMenu使用DefaultMenuLayout时发生,这是此处的L&F默认值。此布局使JPopupMenu无效,然后调用checkRequests(),JPopupMenu的其他布局则不会
  • checkRequests()假定无效弹出窗口的任何可见子项也将无效,并请求其首选大小。但是在这种情况下,JTextArea子组件是有效的,因此当调用其getPreferredSize()时,它和1中的条件都是有效的。发生时,它使自身及其父组件无效,将JPopupMenu的xChildren变量设置为null并导致崩溃
  • 由此看来,可能的解决办法似乎是:

  • 如垃圾神的回答所述,更改JPopupMenu的布局类型
  • 在调用getPreferredSize()之前,使JPOppMenu的子项可见
  • 通过在调用JPOppMenu上的getPreferredSize()之前调用子组件上的getPreferredSize(),隐藏并显示组件,或者显式设置大小,确保布局在3之前更新
  • 使JTextArea无效,该区域确保即使在getPreferredSize()内的布局发生更改时,父组件也不会更新
  • 出现这种情况的原因是show方法调整JPOppMenu的JTextArea子组件的宽度(在本例中,因为组件具有setLineWrap(true),show()将宽度从0更改为100)。在此状态下,JTextArea上对getPreferredSize()的第一次调用将更新其父组件的大小,为了导致崩溃,此第一次调用必须发生在JPopupMenu getPreferredSize()方法内部
  • 此崩溃仅在JPOppMenu使用DefaultMenuLayout时发生,这是此处的L&F默认值。此布局使JPopupMenu无效,然后调用checkRequests(),JPopupMenu的其他布局则不会
  • checkRequests()假定无效弹出窗口的任何可见子项也将无效,并请求其首选大小。但是在这种情况下,JTextArea子组件是有效的,因此当调用其getPreferredSize()时,它和1中的条件都是有效的。发生时,它使自身及其父组件无效,将JPopupMenu的xChildren变量设置为null并导致崩溃
  • 由此看来,可能的解决办法似乎是:

  • 如垃圾神的回答所述,更改JPopupMenu的布局类型
  • 在调用getPreferredSize()之前,使JPOppMenu的子项可见
  • 通过在调用JPOppMenu上的getPreferredSize()之前调用子组件上的getPreferredSize(),隐藏并显示组件,或者显式设置大小,确保布局在3之前更新
  • 使JTextArea无效,该区域确保即使在getPreferredSize()内的布局发生更改时,父组件也不会更新

  • 在BoxLayout源代码中,
    checkRequests()
    遍历组件的所有子组件,检查其所有大小要求。也许您的代码是在创建JPopUp菜单objecdt时,但在创建和呈现其所有子组件之前执行此操作的。在BoxLayout源代码中,
    checkRequests()
    遍历组件的所有子组件,检查其所有大小要求。也许您的代码是在创建JPopUp菜单objecdt时,但在创建和呈现其所有子组件之前执行此操作的。只是一个噱头。这得到了一个支持票,因为它指出了问题的根本原因,但我不能接受它作为答案,因为正如你所说的,有其他方法可以改变布局,但在很多情况下这不是一个选项。重量级contianers必须是pack(),也许要更改默认布局管理器这得到了一个赞成票,因为它指出了问题的根本原因,但我不能接受它作为答案,因为正如你所说的,在很多情况下,更改布局是没有选择余地的。重量级contianers必须是pack(),可能要更改默认布局管理器,您可以在EDT上对
    .getPreferredSize()
    调用进行排队,这将足够延迟以防止错误。或者您可以在EDT上对
    .getPreferredSize()
    调用进行排队,这将足够延迟以防止错误。