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

时间:2009-12-03 23:02:16

标签: java swing awt invokelater

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

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

无论如何,我使用两个 JLabel 显示一个简单的 JFrame ,而另一个线程查询数据库。这一切都发生在程序的开始。问题是我的小“请等待” JFrame 在等待期间出现了它的框架,但没有胆量,没有背景,也没有 JLabel ,这是其余部分它显示的后续程序加载,而不是数据库线程),但到那时它缺少了它的观点。

我编写了以下示例程序。它显示一个简单的 JFrame (CheckingMessagesGUI:一个带有两个 JLabel JFrame ,仅此而已)休眠5秒然后显示示例(主程序) ) JFrame ,然后在这个例子中立即关闭(System.exit(0)),当然我的真实程序继续做更多。我发现invokeLater似乎导致了这个问题。一旦睡眠定时器用完,窗口就会显示,但显示它的代码是在Thread.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);
        /*      }
        });
        */
    }
}

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

5 个答案:

答案 0 :(得分:4)

invokeLater在Event Dispatch Thread中运行Runnable,它也用于更新GUI 你的睡眠阻止了这个线程,所以GUI也没有得到服务,在你从invokeLater代码返回之前不能进行任何更新。
这就是为什么你不应该在这个线程中做任何长(耗时)的计算。它们应该在不同的(新)线程中完成。

The Event Dispatch Queue

  

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

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

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();
}

编辑: 让我们更深入一点(这是我对Swing / AWT工作的看法) 我想应该在CheckingMessagesGUI类中显示“please wait”(参见注释),但不是。
这与GUI的工作方式有关。如果调用相应的(Swing)方法(draw,setText,setLocation,...),它不会直接更改显示内容。它只是在事件队列中排队一个事件。 Event Dispatch Thread(应该是)唯一读取此队列并处理事件的Thread。只要它被阻止 - 在这种情况下通过睡眠 - 将不会显示对GUI的任何更改。 GUI被冻结了。

EDIT2:
invokeLater Runnable 附加到队列的末尾,后者在处理完所有挂起事件后由EDT执行,执行invokeLater调用后的下一个命令。 /> invokeAndWait与上面相同,但实际的线程阻塞直到EDT执行Runnable(在挂起事件之后),也就是说,invokeAndWait之后的命令只会在执行提交的Runnable之后开始。

答案 1 :(得分:1)

  我了解的目的   InvokeLater是这样运行的项目   在正确的AWT事件线程

这是正确的。

然而,这也意味着Thread.sleep()正在EDT上执行,这意味着GUI无法重绘自己,因为你刚刚告诉EDT要睡觉。您需要为长时间运行的任务使用单独的线程。

阅读Concurrency上Swing教程中的部分,了解有关EDT的更多信息。

  

但我的观点是我的“请等待”   (CheckingMessagesGUI)应该有   在我之前已经完全被吸引   叫睡觉。这不应该是真的吗?

以下是我对该过程的简化理解。创建并显示框架,因为它是OS本机组件。但是,contentPane和子组件是轻量级组件,这意味着Swing Repaint Manager会在重新绘制它们时进行计划。因此,在重新计划之前,EDT会进入睡眠状态,直到睡眠完成后才能进行重新绘制。

您可以在Paintng in AWT and Swing上的文章中找到有关Repaint Manager的更多信息。

答案 2 :(得分:0)

我的回答是,当构建GUI时,它不会在那时自动绘制,而是将对paint的调用放在EDT队列中。如果在同一个方法中你构造一个GUI对象,并且setVisible(true)那么在接下来的几行中做一些密集的事情,它会阻止将来调用paint发生,因为在该方法之前它不会被放入EDT队列(用密集的东西)完成。同样如前所述,框架或边框位于等式的平台一侧(因此被绘制),其余的(Jlabel,容器,背景等)在java端,并且直到paint实际运行才会发生(即EDT队列到达它)。我的示例代码在没有InvokeLater调用的情况下工作,因为它在init线程中运行了密集的东西,并允许EDT线程仍然绘制。

答案 3 :(得分:0)

不可见的组件未涂漆。

答案 4 :(得分:0)

这是像我这样的新手的一般解决方案,他们在Swing教程中找到了他们需要的东西。

public void method(){
    final PleaseWaitWindow window = new PleaseWaitWindow();

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            //stuff that you want to do that is preventing window to display

            window.dispose();
        }
    }
    thread.start();
}