我有一個 Java3D 場景圖更新任務,在我的筆記本電腦上需要 3-4 秒才能完成。在此期間,我希望顯示一個視窗,要求用戶等到作業完成。我有一個類似的情況,需要更長的時間,而我使用 SwingWorker 的實施作業正常。但是當我將代碼應用于此更新操作時,對話框根本不會出現。
這是我要顯示的 WaitDialog(我也嘗試從 JDialog 繼承 - 沒有變化)
public class WaitDialog extends JFrame {
private static final long serialVersionUID = 1L;
protected int rows = 2;
protected int cols= 1;
protected int rowSpace= 12;
protected int colSpace= 12;
public WaitDialog(Component parent, String message) {
JPanel panel = new JPanel(new GridLayout(rows, cols, rowSpace, colSpace));
panel.setBorder( new EmptyBorder(12, 12, 12, 12) );
panel.add(new JLabel(message), BorderLayout.CENTER);
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
panel.add(progressBar);
setUndecorated(true);
setAlwaysOnTop(true);
getContentPane().add(panel);
pack();
setLocationRelativeTo(parent);
setVisible(true);
}
public void close() {
setVisible(false);
dispose();
}
}
讓我頭疼的方法是這樣的……
public Result doIt(String fileName) throws Exception {
WaitDialog waitDialog = new WaitDialog(null, "Update...");
InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
Result result = ansicht.calculateResult(inputStream); // Java3D calculation
waitDialog.close();
return result;
}
在此版本中,未顯示 waitDialog 但 calculteResult 作業正常。因此,如上所述,我從另一個地方復制了作業邏輯,看起來像這樣......
public Result doIt(String fileName) throws Exception {
WaitDialog waitDialog = new WaitDialog(null, "Update...");
SwingWorker<Result,Void> worker = new SwingWorker<Result,Void>() {
@Override
protected Result doInBackground() throws Exception {
InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
Result result = ansicht.calculateResult(inputStream); // Java3D calculation
return result;
}
@Override
protected void done() {
waitDialog.close();
}
};
worker.execute();
Result result = worker.get();
return result;
}
這顯示沒有效果。沒有顯示任何訊息,并且 calculateResult 仍然可以正常作業。所以我在網上查看了我可能有哪些替代方案并嘗試了這個......
public Result doIt(String fileName) throws Exception {
WaitDialog waitDialog = new WaitDialog(null, "Update...");
InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
SwingUtilities.invokeAndWait(() -> {
try {
result = ansicht.calculateResult(inputStream); // Java3D calculation
} catch (Exception e) {
e.printStackTrace();
}
});
waitDialog.close();
return result;
}
結果現在是一個類變數。這個版本顯示了等待對話框,但是 calculateResult 呼叫沒有回來,對話框也沒有消失。
我還嘗試了一個帶有老式執行緒的版本,我在網上找到了它......
public Result doIt(String fileName) throws Exception {
WaitDialog waitDialog = new WaitDialog(null, "Update...");
Thread thread = new Thread(() -> {
SwingUtilities.invokeAndWait(() -> {
try {
InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
result = ansicht.calculateResult(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
});
thread.start();
while(thread.isAlive()) {};
waitDialog.close();
return result;
}
也沒有做我想做的事。
你能告訴我怎么做嗎?先感謝您。
uj5u.com熱心網友回復:
swing 的作業方式是您擁有不斷運行和處理 GUI 事件的事件調度執行緒 (EDT)。當你有一個長時間運行的任務時,你在不同的執行緒上運行它,然后告訴 EDT 你已經完成了。
EDT 上是否呼叫了“doIt”?例如,您是從按鈕之類的事件開始的嗎?我會假設它是。
在結果完成之前,您的所有版本都會阻止“doIt”。你需要讓 doIt 完成。我建議回電。
public void processResult( Result result){
//this should be a continuation of the code from where you call doIt
}
然后在第二個版本中你可以更新它。
public void doIt(String fileName) throws Exception {
WaitDialog waitDialog = new WaitDialog(null, "Update...");
SwingWorker<Result,Void> worker = new SwingWorker<Result,Void>() {
@Override
protected Result doInBackground() throws Exception {
InputStream inputStream = new BufferedInputStream(new FileInputStream(fileName));
Result result = ansicht.calculateResult(inputStream); // Java3D calculation
return result;
}
@Override
protected void done() {
waitDialog.close();
processResult( get() );
}
};
worker.execute();
//this is blocking and preventing your gui from updating.
//Result result = worker.get();
//return result;
}
這是您代碼的最差版本。
SwingUtilities.invokeAndWait(() -> {
try {
result = ansicht.calculateResult(inputStream); // Java3D calculation
} catch (Exception e) {
e.printStackTrace();
}
});
您實際上是在將長時間運行的任務發布到 EDT 并阻止當前執行緒,我懷疑這也是 EDT。
uj5u.com熱心網友回復:
除了(并希望支持)馬特的回答。
Swing 不是執行緒并且是單執行緒的,這意味著您應該嘗試從事件調度執行緒的背景關系之外更新 UI 的狀態(或 UI 依賴的任何內容),或者從背景關系中執行長時間運行或阻塞操作事件調度執行緒。
有關更多詳細資訊,請參閱Swing 中的并發。
這里的重點是SwingWorker#get方法。JavaDocs 狀態:
如有必要,等待計算完成,然后檢索其結果。
注意:在 Event Dispatch Thread 上呼叫 get 會阻止處理所有事件,包括重繪,直到此 SwingWorker 完成。
當您希望 SwingWorker 阻塞 Event Dispatch Thread 時,我們建議您使用模式對話框。
這將大大有助于解釋您的問題。
以下是另一種可能的方法。它利用專用SwingWorker...
public class MagicWorker extends SwingWorker<String, Void> {
@Override
protected String doInBackground() throws Exception {
// This is where the magic happens
Thread.sleep(5000);
return "Rabbit";
}
}
并通過使用PropertyChangeListener...進行監控
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
dialog.dispose();
try {
String magic = worker.get();
observer.magicDidHappen(magic);
} catch (InterruptedException | ExecutionException ex) {
observer.magicDidFailToHappen(ex);
}
}
}
});
并利用觀察者(AKA 監聽器)通知呼叫者操作完成或失敗,以便相關方處理操作結果。
重要的是要注意,這只是您可能實作此目的的一種方式,而 matt 的解決方案也是一個有效的解決方案!
可運行的示例...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
public interface MagicObserver {
public void magicDidHappen(String result);
public void magicDidFailToHappen(Exception exp);
}
public MainPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new GridBagLayout());
JButton makeMagic = new JButton("Make the magic happen");
add(makeMagic);
makeMagic.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
makeMagic.setEnabled(false);
makeMagic(new MagicObserver() {
@Override
public void magicDidHappen(String result) {
makeMagic.setEnabled(true);
JOptionPane.showMessageDialog(MainPane.this, result, "Magic", JOptionPane.INFORMATION_MESSAGE);
}
@Override
public void magicDidFailToHappen(Exception exp) {
makeMagic.setEnabled(true);
exp.printStackTrace();
JOptionPane.showMessageDialog(MainPane.this, "Failed to make magic", "Error", JOptionPane.ERROR_MESSAGE);
}
});
}
});
}
protected void makeMagic(MagicObserver observer) {
MagicWorker worker = new MagicWorker();
JDialog dialog = new JDialog(SwingUtilities.windowForComponent(this));
dialog.add(new WaitPane());
dialog.setModal(true);
dialog.setUndecorated(true);
dialog.pack();
dialog.setLocationRelativeTo(this);
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
dialog.dispose();
try {
String magic = worker.get();
observer.magicDidHappen(magic);
} catch (InterruptedException | ExecutionException ex) {
observer.magicDidFailToHappen(ex);
}
}
}
});
worker.execute();
dialog.setVisible(true);
}
}
public class WaitPane extends JPanel {
public WaitPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new GridBagLayout());
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(8, 8, 8, 8);
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("Making the magic happen"), gbc);
add(progressBar, gbc);
}
}
public class MagicWorker extends SwingWorker<String, Void> {
@Override
protected String doInBackground() throws Exception {
// This is where the magic happens
Thread.sleep(5000);
return "Rabbit";
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/491726.html
