Java 制作一个显示“请稍候”JDialog 的摆动线程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20269083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Make a swing thread that show a "Please Wait" JDialog
提问by user2572526
The problem is this:
I've a swing application running, at a certain point a dialog requires to insert username and password and to press "ok".
I would like that when the user press "ok" the swing application does in this order:
问题是这样的:
我正在运行一个 Swing 应用程序,在某个时候,对话框需要插入用户名和密码并按“确定”。
我希望当用户按“确定”时,swing 应用程序按以下顺序执行:
- Open a "Please wait" JDialog
- Make some operation(eventually displaying some other JDialog or JOptionPane)
- When it finishes with the operation close the "please wait" JDialog
- 打开“请稍候”JDialog
- 进行一些操作(最终显示一些其他的 JDialog 或 JOptionPane)
- 操作完成后关闭“请稍候”JDialog
This is the code that I wrote in the okButtonActionPerformed():
这是我在 okButtonActionPerformed() 中编写的代码:
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {
//This class simply extends a JDialog and contains an image and a jlabel (Please wait)
final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);
waitDialog.setVisible(true);
... //Do some operation (eventually show other JDialogs or JOptionPanes)
waitDialog.dispose()
}
This code obviously doesn't works because when I call the waitDialog in the same thread it blocks all till I don't close it.
So I tried to run it in a different thread:
这段代码显然不起作用,因为当我在同一个线程中调用 waitDialog 时,它会阻塞所有直到我不关闭它。
所以我尝试在不同的线程中运行它:
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {
//This class simply extends a JDialog and contains an image and a jlabel (Please wait)
final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
waitDialog.setVisible(true);
}
});
... //Do some operation (eventually show other JDialogs or JOptionPanes)
waitDialog.dispose()
}
But also this doesn't work because the waitDialog is not displayed immediately but only after that the operation completed their work (when they show a joption pane "You are logged in as...")
但这也不起作用,因为 waitDialog 不会立即显示,而是在操作完成后才显示(当他们显示 joption 窗格“您登录为...”时)
I also tried to use invokeAndWait instead of invokeLater but in this case it throws an exception:
我也尝试使用 invokeAndWait 而不是 invokeLater 但在这种情况下它会引发异常:
Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
How can I do?
我能怎么做?
采纳答案by Hovercraft Full Of Eels
Consider using a SwingWorker to do your background work, and then closing the dialog either in the SwingWorker's done()
method or (my preference) in a PropertyChangeListener that is added to the SwingWorker.
考虑使用 SwingWorker 来完成后台工作,然后在 SwingWorker 的done()
方法中或(我的偏好)在添加到 SwingWorker 的 PropertyChangeListener 中关闭对话框。
e.g.,
例如,
import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class PleaseWaitEg {
public static void main(String[] args) {
JButton showWaitBtn = new JButton(new ShowWaitAction("Show Wait Dialog"));
JPanel panel = new JPanel();
panel.add(showWaitBtn);
JFrame frame = new JFrame("Frame");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ShowWaitAction extends AbstractAction {
protected static final long SLEEP_TIME = 3 * 1000;
public ShowWaitAction(String name) {
super(name);
}
@Override
public void actionPerformed(ActionEvent evt) {
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>(){
@Override
protected Void doInBackground() throws Exception {
// mimic some long-running process here...
Thread.sleep(SLEEP_TIME);
return null;
}
};
Window win = SwingUtilities.getWindowAncestor((AbstractButton)evt.getSource());
final JDialog dialog = new JDialog(win, "Dialog", ModalityType.APPLICATION_MODAL);
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
dialog.dispose();
}
}
}
});
mySwingWorker.execute();
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
}
Notes:
笔记:
- A key concept is to set everything up, add the PropertyChangeListener, get the SwingWorker running, all beforedisplaying the modal dialog, because once the modal dialog is shown, all code flow from the calling code is frozen (as you've found out).
- Why do I prefer the PropertyChangeListener to using the done method (as Elias demonstrates in his decent answer here, which I've up-voted) -- using the listener provides more separation of concerns, looser coupling. This way the SwingWorker has to know nothing of the GUI code that is using it.
- 一个关键概念是设置所有内容,添加 PropertyChangeListener,运行 SwingWorker,所有这些都在显示模态对话框之前,因为一旦显示模态对话框,来自调用代码的所有代码流都会被冻结(如您所见) .
- 为什么我更喜欢 PropertyChangeListener 而不是使用 done 方法(正如 Elias 在他这里的体面答案中所展示的那样,我已经投票赞成)——使用侦听器提供了更多的关注点分离,更松散的耦合。这样 SwingWorker 就不必知道正在使用它的 GUI 代码。
回答by elias
public void okButtonActionPerformed(ActionEvent e) {
final JDialog loading = new JDialog(parentComponent);
JPanel p1 = new JPanel(new BorderLayout());
p1.add(new JLabel("Please wait..."), BorderLayout.CENTER);
loading.setUndecorated(true);
loading.getContentPane().add(p1);
loading.pack();
loading.setLocationRelativeTo(parentComponent);
loading.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
loading.setModal(true);
SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
@Override
protected String doInBackground() throws InterruptedException
/** Execute some operation */
}
@Override
protected void done() {
loading.dispose();
}
};
worker.execute();
loading.setVisible(true);
try {
worker.get();
} catch (Exception e1) {
e1.printStackTrace();
}
}
回答by Gerardo Neto
A variation of the above answer
上述答案的变体
It's an easy and replicable way to do...
这是一种简单且可复制的方法...
//This code goes inside your button action
DialogWait wait = new DialogWait();
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
//Here you put your long-running process...
wait.close();
return null;
}
};
mySwingWorker.execute();
wait.makeWait("Test", evt);
//end
//Create this class on your project
class DialogWait {
private JDialog dialog;
public void makeWait(String msg, ActionEvent evt) {
Window win = SwingUtilities.getWindowAncestor((AbstractButton) evt.getSource());
dialog = new JDialog(win, msg, Dialog.ModalityType.APPLICATION_MODAL);
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
public void close() {
dialog.dispose();
}
}