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

时间:2013-11-21 23:38:36

标签: 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 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的堆栈。是否可能在此处调用另一个线程,或者布局中缺少某些东西?具有此跟踪的唯一bug report在我的Java环境中标记为已修复。

2 个答案:

答案 0 :(得分:3)

  1. 出现这种情况是因为show方法调整了JPOupMenu的JTextArea子组件的宽度(在此示例中,因为组件具有setLineWrap(true),show()将宽度宽度从0更改为100)。在这种状态下,第一次调用JTextArea上的getPreferredSize()将更新其父组件的大小,导致第一次调用崩溃必须在JPopupMenu getPreferredSize()方法内发生。
  2. 此崩溃只能在JPopupMenu使用DefaultMenuLayout时发生,这是此处的L& F默认值。此布局使JPopupMenu无效,然后调用checkRequests(),而JPopupMenu的其他布局则不会。
  3. checkRequests()假定无效弹出窗口的任何可见子窗口也无效并请求其首选大小的不变量。但是在这种情况下,JTextArea子组件是有效的,因此当调用其getPreferredSize()并且条件为1.时,它会使自身及其父组件无效,将JPopupMenu的xChildren变量设置为null并导致崩溃。 / LI>

    由此可能的解决方案似乎是:

    1. 更改JPopupMenu的布局类型,如trashgod的答案中所述。
    2. 在调用getPreferredSize()之前使JPopupMenu的子项可见。
    3. 确保布局在3.之前更新,或者通过在JPopupMenu上调用getPreferredSize()之前调用子组件上的getPreferredSize(),隐藏和显示组件,或者通过显式设置大小。
    4. 使JTextArea无效,这确保即使布局在getPreferredSize()内部发生更改,父组件也不会更新。

答案 1 :(得分:2)

这似乎是DefaultMenuLayout的一项功能,见here。它扩展BoxLayout并期望Container推迟的非空目标BasicPopupMenuUI。一种替代方法是使用不同的布局,例如:

basicPopupMenu.setLayout(new GridLayout());