【问题标题】:1x1 dimension of modal JDialog模态 JDialog 的 1x1 尺寸
【发布时间】:2012-06-06 04:42:16
【问题描述】:

问题

我们的独立 Swing 应用程序在某些特定事件(按钮单击等)处显示 模态 JDialog 该对话框包含一些其他 Swing 组件(JLabels、JButtons、...)。而不是通过JDialog.setBounds(...) 方法显式设置其维度,我们调用JDialog.pack()(或JOptionPane 的showDialog(...) 方法隐式调用它)。通过pack() 方法计算的尺寸始终是恒定的(例如 300x100 像素。)不幸的是,有时可见对话框的实际尺寸是 1x1 像素(JDialog.getSize().equals(new Dimension(1, 1)))。

JDialog 初始化

我们在应用程序中初始化JDialogs 有两种方式。 我们检查了这两个初始化方法总是从 EventDispatchThread 调用。

第一种方法

我们只是创建一个ADialog 的实例,它是JDialog 的子类。这是我们初始化过程的 sn-p:

ADialog dialog = new ADialog();
dialog.setContentPane(content);
dialog.setVisible(true);

这是我们的ADialog 实现:

public class ADialog extends JDialog implements ComponentListener {

    public JfosDialog(Frame owner) {
        super(owner);
        init();
    }

    private void init() {
        super.addComponentListener(this);
    }

    @Override
    public void componentShown(ComponentEvent e) {
        // Calling pack() at this place is really weird, but we
        // have to do it since some subclasses put their
        // content to dialog in overriden componentShown().
        pack();
    }

    @Override
    public void componentMoved(ComponentEvent e) { /** not interested */ }

    @Override
    public void componentResized(ComponentEvent e) { /** not interested */ }
}

第二种方法

public JOptionPane showDialog(...) {
    JOptionPane jop = new JOptionPane(message, msgType, option, null, textMessages);
    JDialog dialog = jop.createDialog(owner, titleMsg);
    setDialogTraversal(dialog);
    dialog.setVisible(true);
    dialog.dispose();
    return jop;
}

重现性

环境

  • Ubuntu 12.04., JRE 1.6 / JRE 1.7 / OpenJDK 1.6
  • Windows XP、JRE 1.6.u16

用第一种初始化方法重现问题

我们只显示然后隐藏对话框 N 次 (0 java.awt.Robot 脚本来显示和隐藏循环中的对话框。这比手动操作更舒服。

用第二种初始化方法重现问题

步骤与第一种方法相同:只显示然后隐藏对话框N次。不幸的是,我们无法在我们的开发环境中重现它,但它可以很容易地在生产 PC 中重现(CPU 与我们的开发环境不同,安装了一些防病毒软件会永久造成系统负载等)

到目前为止,我们无法在某种测试/示例项目中重现该问题。这可能表明我们的应用程序有问题。但是,似乎问题可能出在 Swing 的本机代码中(请参阅跟踪部分)。

追踪

我们在 64 位 OpenJDK 1.6.0_24 中追踪了问题的原因。我们发现JDialog 的维度是由XToolkit 触发的XConfigureEvent 修改的。该事件是在从本地方法调用返回后在XToolkit.run(boolean) 方法的事件循环中构造的。

为了简化事情,我在这里只发布了一个代码 sn-p,说明了 XToolkit 的事件循环机制以及我们的跟踪结果。您可能还会看到full source code here。

public class XToolkit ... {
    ...
    public void run(boolean loop) {
        XEvent ev = new XEvent();
        while(true) {
            awtLock();
            try {
                if (loop == SECONDARY_LOOP) {
                    ...
                } else {
                    ...

                    // ===========================================
                    // The following invocation of native method sometimes
                    // updates ev object in a way that ev.get_type() method returns value 22
                    // indicating that the event's type is XConfigureEvent.
                    // In such case, as I mentioned in text above, the value of
                    // ev.get_xconfigure().get_width() / .get_height()
                    // is sometimes 1.

                    XlibWrapper.XNextEvent(getDisplay(),ev.pData); // <-----

                    // ===========================================
                }
                ...
                // The XConfigureEvent with get_width() == 1 and
                // get_heigth() == 1 is dispatcher here:
                dispatchEvent(ev); // <-----
                ...
            } catch (...) {
                ...
            }
        }
    }
    ...
}

您有任何想法如何修复此错误/更深入地跟踪它/更健壮地重现它...?

我很欣赏任何想法,因为这个错误真的很痛苦。

【问题讨论】:

  • 另见Initial Threads。
  • 正如 (@trashgod) 所提到的,您可以做任何事情,以任何方式准备您的 GUI,但是 setVisible 必须在 invokeLater 中,其余方法对于 EDT 和屏幕可见性并不重要,特别是如果 JDialog 和 JOptionPane 从 EDT 中调用,您只能在屏幕上看到工具栏,RootPane 不可见
  • @mKorbel 的 Q&A 引用了两种检测 EDT 违规的方法。
  • 我们检查了所有的初始化方法并且在 EDT 中调用了 setVisible(true)。我还修改了我的帖子并添加了我们的初始化代码。
  • 你有在componentShown方法中将其内容添加到对话框的子类的例子吗?

标签: java swing


【解决方案1】:

pack() 应该遵循组件的首选(或最小大小,取决于布局管理器)。

尝试为对话框中的组件明确设置最小和首选大小,看看是否能解决问题。

就竞争条件而言,也可能是代码中的不正确初始化。当布局管理器查询它们的首选(或最小尺寸)时,大多数组件都会优化它们的内容(例如 JLabel 的文本)。因此,填充组件的代码可能并不总是以相同的顺序运行。您是否确保在 AWT 线程上创建对话框和所有包含的组件?

【讨论】:

  • 我在帖子中添加了一些关于我们的初始化程序的信息。我们检查了 JDialogs 是否在 EventDispatchThread 中创建和操作。
猜你喜欢
  • 2021-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-17
  • 1970-01-01
  • 2021-07-21
  • 2020-12-26
  • 1970-01-01
相关资源
最近更新 更多