![](/img/trans.png)
[英]How do I implement the codes which works as a timer app using awt,swing,Thread while satisfying some conditions?
[英]Swing: How do I run a job from AWT thread, but after a window was layed out?
我完整的GUI在AWT線程中運行,因為我使用SwingUtilities.invokeAndWait(...)
啟動了主窗口。
現在,我有了一個JDialog,它僅顯示一個JLabel
,它指示某個作業正在進行中,並在作業完成后關閉該對話框。
問題是:未顯示標簽。 這項工作似乎在JDialog
完全布局之前就開始了。
當我只是讓對話,而無需等待工作,關閉打開的, 則顯示的標簽。
對話框在其ctor中所做的最后一件事是setVisible(true)
。
諸如revalidate()
, repaint()
……之類的方法也無濟於事。
即使我為受監控的作業啟動線程,並使用someThread.join()
等待它,也無濟於事,因為我猜想當前線程(即AWT線程)被join
阻塞了。
用JFrame
替換JDialog
也無濟於事。
那么,這個概念通常是錯誤的嗎? 或者,在確保JDialog
(或JFrame
)已完全布局后 ,我可以管理它完成某些工作嗎?
我要實現的簡化算法:
JDialog
的子類 我設法寫了一個可重現的測試用例:
編輯現在解決了一個答案的問題:該用例確實顯示了標簽,但是由於對話框的模態,它在“模擬過程”之后無法關閉。
import java.awt.*;
import javax.swing.*;
public class _DialogTest2 {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JLabel jLabel = new JLabel("Please wait...");
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
JDialog d = new JDialog(myFrame, "I'm waiting");
d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); // simulate process
jLabel.setText("Done");
} catch (InterruptedException ex) {
}
}
});
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
}
}
嘗試這個:
package javaapplication3;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args)
throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JLabel jLabel = new JLabel("Please wait...");
@Override
public void run() {
JFrame myFrame = new JFrame("Main frame");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
JDialog d = new JDialog(myFrame, "I'm waiting");
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
new Thread(new Runnable() {
@Override
public void run() {
public void run() {
try {
Thread.sleep(3000); // simulate process
jLabel.setText("Done"); // HERE: should be done on EDT!
} catch (InterruptedException ex) {
}
}
}).start();
}
});
}
}
這是可行的,但這是不正確的。 我會解釋發生了什么。
您的main()
方法從“ main”線程開始。 所有與Swing相關的代碼都應在EDT線程上完成。 這就是為什么您(正確)使用SwingUtilities.invokeAndWait(...)
。 到現在為止還挺好。
但是,EDT上不應有長期運行的任務。 由於Swing是單線程的,因此任何長時間運行的進程都會阻塞EDT。 因此,永遠不要在EDT上執行您的代碼Thread.wait(...)
。 這是我的修改。 我將調用包裝在另一個線程中。 因此,這是Swing慣用的長期運行任務處理。 為了簡潔起見,我使用Thread類,但我確實建議您使用SwingWorker
線程。
非常重要:在前面的示例中,我犯了一個錯誤。 看到帶有“ HERE”注釋的行? 這是另一個Swing單線程規則沖突。 線程內的代碼在EDT 外部運行,因此永遠不要觸摸Swing。 因此,此代碼對於Swing單線程規則不正確。 凍結GUI是不安全的。
如何糾正呢? 簡單。 您應該將調用包裝在另一個線程中,並將其放在EDT隊列中。 因此正確的代碼應如下所示:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jLabel.setText("Done");
}
});
編輯 :這個問題觸及很多Swing相關問題。 無法一次全部解釋...但是下面是另一個片段,它可以滿足您的需求:
public static void main(String[] args)
throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
final JFrame myFrame = new JFrame("Main frame");
final JLabel jLabel = new JLabel("Please wait...");
final JDialog d = new JDialog(myFrame, "I'm waiting");
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
d.add(jLabel);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); // simulate process
System.out.println("After");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
} catch (InterruptedException ex) {
}
}
}).start();
d.setVisible(true);
}
});
}
總結一下:
SwingUtilities.
在EDT上運行代碼SwingUtilities.
...
invokeAndWait()
-顧名思義,調用是同步的, invokeLater()
-調用代碼“有時”,但立即返回 Thread
(將Runnable傳遞給新線程或覆蓋它的start()
方法)並啟動它, SwingWorker
線程。 典型的GUI場景涉及:
1.,2、3和4.在EDT上運行。 4.不應該。 有許多方法可以編寫適當的線程代碼。 最麻煩的是使用Java早期版本附帶的Thread
類。 如果天真地這樣做,則會浪費資源(一次運行的線程太多)。 另外,更新GUI也很麻煩。 使用SwingWorker可以稍微減輕問題。 在啟動,運行和更新GUI時,可以保證其行為正常(每個都有專用的方法,您可以重寫並確保它在正確的線程上運行)。
如前面的回答所述,您必須在與EDT不同的線程中運行冗長的作業。
最簡單的恕我直言是使用SwingWorker並添加一個偵聽器以在完成后放置對話框。 這是一個示例:
SwingWorkerCompletionWaiter.java
public class SwingWorkerCompletionWaiter implements PropertyChangeListener {
private JDialog dialog;
public SwingWorkerCompletionWaiter(JDialog dialog) {
this.dialog = dialog;
}
@Override
public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName())
&& SwingWorker.StateValue.DONE == event.getNewValue()) {
dialog.setVisible(false);
dialog.dispose();
}
}
}
以下代碼將運行一些冗長的任務:
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// do something long
}
};
JDialog dialog = new JDialog();
// initialize the dialog here
worker.addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog));
worker.execute();
dialog.setVisible(true);
而且,如果您想創建某種“請稍候”對話框,則只需擴展JDialog並在需要的地方使用擴展的類可能會更容易。
首先,很抱歉對工作有所猜測,在發布代碼之前,我並沒有真正理解您要執行的操作。
盡管如此,我對創建對話框,窗口和常規對話框的方式並不陌生,因此建議您為JDialog創建一個單獨的類。 只是一個簡單的類,就像這樣:
public class MyDialog extends JDialog
{
public MyDialog(JFrame owner, String title)
{
super(owner, title);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
d.setDefaultCloseOperation(DISPOSE_ON_CLOSE );
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(new JLabel("Please wait..."));
}
}
並以這種方式啟動對話框:
new MyDialog(this, "Your title");
它可能看起來像狗屎,但可能有效(我尚未測試)。
希望這可以幫助。
您需要在后台線程或主應用程序線程中運行作業。 應該使用SwingUtilities.invokeLater();
在事件調度線程(EDT)上創建JDialog和JFrame SwingUtilities.invokeLater();
。 然后等待作業完成,然后關閉(EDT)中的對話框。 由於布局和作業位於兩個單獨的線程上,因此應該沒有問題。 實際上,對示例的以下修改有效:
public static void main(String[] args) throws Exception {
final JFrame myFrame = new JFrame("Main frame");
final JDialog d = new JDialog(myFrame, "I'm waiting");
final Thread backgroundJob = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000); // simulate process
}
catch (InterruptedException ex) {
Logger.getLogger(NewClass.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
backgroundJob.start();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.add(new JLabel("Please wait..."));
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
}
});
backgroundJob.join();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
d.setVisible(false);
}
});
}
您應該使用invokeLater代替InvokeAndWait。
public class DialogTest {
public static void main(String[] args) throws Exception {
final JLabel comp = new JLabel("the job has started");;
final JFrame myFrame = new JFrame("Main frame");
final JDialog d = new JDialog(myFrame, "I'm waiting");
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(750, 500);
myFrame.setLocationRelativeTo(null);
myFrame.setVisible(true);
d.setModalityType(JDialog.ModalityType.APPLICATION_MODAL);
d.add(comp);
d.setSize(300, 200);
d.setLocationRelativeTo(null);
d.setVisible(true);
}
});
try {
Thread.sleep(3000);
// d.setVisible(false);
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
comp.setText("the job has finished");
d.setVisible(false);
d.dispose();
myFrame.setVisible(false);
myFrame.dispose();
}
});
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.