为什么InvokeLater导致我的JFrame无法正确显示?

Mar*_*arc 6 java swing awt invokelater

好的,我已经在网上阅读了一个搜索过的内容,但我还没有找到问题的解决方案,也许我错过了一些简单的东西,因此我在这里......

我有一个相当大的项目,处理维修业务的工单.这是所有数据库连接,许多页面的代码和类.但我只是在前端添加了一小段代码,实质上是在我们的笔记区域中检查新消息.

无论如何,我显示一个带有两个JLabel的简单JFrame,而一个单独的线程查询数据库.这一切都发生在程序的开始.问题是我的小"请等待" JFrame提出了它的框架,但没有胆量,没有背景,没有JLabel,在等待期间(这是程序加载的其余部分,而不是数据库线程),显示后,但到那时它错过了它的观点.

我写了以下示例程序.它显示一个简单的JFrame(CheckingMessagesGUI:一个带有两个JLabelJFrame,仅此而已)休眠5秒然后显示Example(主程序)JFrame,然后立即关闭()在这个例子中,当然我真正的程序继续做多很多.我发现这似乎是导致问题的原因.一旦睡眠定时器用完,窗口就会显示,但显示它的代码是在命令之前给出的,应该按照那个顺序完成吗?System.exit(0)invokeLaterThread.sleep

我的问题是为什么invokeLater导致我的JFrame无法正确显示?

我的理解是,目的invokeLater是让项目在正确的AWT事件线程上运行,这会让我觉得这个窗口会被正确绘制.无论如何,我确定我错过了一些明显的东西.我invokeLater在下面的代码中注释了该部分,并且它正确运行,如果你把它放回去它不...

提前谢谢了.

package javaapplication6;

public class Example extends javax.swing.JFrame {          
    public Example() {
        System.out.println("Example started");
        setBounds(100,100,200,200);

        System.out.println("cmGUI instantiated");
        CheckingMessagesGUI cmGUI = new CheckingMessagesGUI();
        System.out.println("Set cmGUI visible");
        cmGUI.setVisible(true);
        cmGUI.validate();
        try {
            System.out.println("timer started");
            Thread.sleep(5000);
            System.out.println("timer done");
        } catch(InterruptedException e){
        }
        System.exit(0);
    }

    public static void main(String[] args) {
        /*java.awt.EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() { */
        System.out.println("Started");
        System.out.println("example Instantiated");
        Example example = new Example();
        System.out.println("example visible");
        example.setVisible(true);
        /*      }
        });
        */
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:为了澄清,我意识到Thread.sleep()会阻止一切,但是在我打电话给睡眠之前,我的CheckingMessagesGUI是否已经完全被绘制了?这就是问题所在.

use*_*421 4

invokeLater 在事件调度线程中运行 Runnable,该线程也用于更新 GUI。
您的睡眠阻塞了该线程,因此 GUI 也无法得到服务,在您从 invokeLater 代码返回之前无法进行任何更新。
这就是为什么您不应该在此线程中进行任何长时间(耗时)计算的原因。它们应该在不同的(新的)线程中完成。

事件调度队列状态

事件派发线程上的任务必须快速完成;如果不这样做,未处理的事件会备份并且用户界面变得无响应。

您的代码可以更改为(未经测试):

public Example(){
    System.out.println("Example started");
    setBounds(100,100,200,200);

    System.out.println("cmGUI instantiated");
    CheckingMessagesGUI cmGUI = new CheckingMessagesGUI();
    System.out.println("Set cmGUI visible");
    cmGUI.setVisible(true);
    cmGUI.validate();

    Thread thread = new Thread(new Runnable() {
        try {
            System.out.println("timer started");
            Thread.sleep(5000);
            System.out.println("timer done");
        } catch(InterruptedException e) {
        }
        System.exit(0);
    });
    thread.start();
}
Run Code Online (Sandbox Code Playgroud)

编辑:让我们更“深入”一点(这是我对 Swing/AWT 工作的看法)。
我认为“请稍候”(请参阅​​注释)应该显示在 CheckingMessagesGUI 类中,但事实并非如此。
这与 GUI 的工作方式有关。如果您调用相应的 (Swing) 方法(draw、setText、setLocation...),它不会直接更改显示屏上的任何内容;它只是将事件放入事件队列中。事件调度线程是(应该是)读取此队列并处理事件的唯一线程。只要它被阻止(在本例中被睡眠阻止),就不会显示 GUI 的任何更改。GUI 被冻结。

EDIT2:
invokeLaterRunnable 被追加到队列末尾,以便在处理完所有待处理事件后由 EDT 执行,将执行 invokeLater 调用后的下一个命令。
invokeAndWait与上面相同,但实际的 Thread 会阻塞,直到 EDT 执行 Runnable(在待处理事件之后),也就是说,invokeAndWait 之后的命令只有在执行提交的 Runnable 后才会启动。