好的,我已经在网上阅读了一个搜索过的内容,但我还没有找到问题的解决方案,也许我错过了一些简单的内容,因此我在这里......
我有一个相当大的项目,处理维修业务的工单。这是所有数据库连接,许多页面的代码和类。但我只是在前端添加了一小段代码,实质上是在我们的笔记区域中检查新消息。
无论如何,我使用两个 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是否已经完全被绘制了?这就是问题所在。
答案 0 :(得分:4)
invokeLater在Event Dispatch Thread中运行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类中显示“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();
}