6

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

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

无论如何,我用两个JLabel显示一个简单的JFrame ,而一个单独的线程查询数据库。这一切都发生在程序开始时。问题是我的小“请稍候” JFrame在等待期间(这是程序加载的其余部分,而不是数据库线程)提出了它的框架,但没有胆量,没有背景,也没有JLabel ,后记它显示,但到那时它就失去了意义。

我编写了以下示例程序。它显示一个简单的JFrame(CheckingMessagesGUI:一个带有两个JLabel的JFrame,仅此而已)休眠 5 秒,然后显示示例(主程序)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);
        /*      }
        });
        */
    }
}

更新:为了澄清,我意识到Thread.sleep()会阻止一切,但我的 CheckingMessagesGUI 不应该在我调用睡眠之前已经完全绘制吗?这就是问题所在。

4

5 回答 5

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

编辑:让我们再“深入一点”(这是我对 Swing/AWT 工作的看法)。
我想“请稍候”(见评论)应该显示在 CheckingMessagesGUI 类中,但不是。
这与 GUI 的工作方式有关。如果您调用相应的 (Swing) 方法(draw、setText、setLocation ...),它不会直接更改显示屏上的任何内容;它只是在事件队列中排队一个事件。Event Dispatch Thread 是(应该是)读取此队列并处理事件的唯一线程。只要它被阻塞 - 在这种情况下被睡眠 - 就不会显示对 GUI 的任何更改。GUI 被冻结。

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

于 2009-12-04T00:03:30.913 回答
1

我的理解是 InvokeLater 的目的是让项目在正确的 AWT 事件线程上运行

那是对的。

但是,这也意味着 Thread.sleep() 正在 EDT 上执行,这意味着 GUI 无法自行重新绘制,因为您只是告诉 EDT 休眠。您需要为长时间运行的任务使用单独的线程。

有关 EDT 的更多信息,请阅读 Swing并发教程中的部分。

但我的意思是,我的“请稍候”(CheckingMessagesGUI)在我调用睡眠之前应该已经完全绘制好了。这不应该是真的吗?

这是我对这个过程的简化理解。框架被创建并显示,因为它是一个操作系统原生组件。但是,contentPane 和子组件是轻量级组件,这意味着 Swing 重绘管理器会安排何时重绘它们。因此,在安排重绘之前,EDT 处于休眠状态,直到休眠完成才能进行重绘。

您可以在有关AWT 和 Swing 中的 Paintng的文章中找到有关重绘管理器的更多信息。

于 2009-12-04T00:00:07.673 回答
1

对于像我这样在 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();
}
于 2010-01-26T11:42:06.333 回答
0

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

于 2009-12-04T03:41:11.660 回答
0

不绘制不可见的组件。

于 2009-12-15T12:58:54.693 回答