制作一个显示“请稍候”JDialog 的摆动线程
Posted
技术标签:
【中文标题】制作一个显示“请稍候”JDialog 的摆动线程【英文标题】:Make a swing thread that show a "Please Wait" JDialog 【发布时间】:2013-12-14 15:58:51 【问题描述】:问题是这样的: 我有一个正在运行的摇摆应用程序,在某个时候对话框需要插入用户名和密码并按“确定”。 我希望当用户按“确定”时,swing 应用程序按以下顺序执行:
-
打开“请稍候”JDialog
进行一些操作(最终显示一些其他的 JDialog 或 JOptionPane)
当它完成操作时关闭“请稍候”JDialog
这是我在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()
这段代码显然不起作用,因为当我在同一个线程中调用 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()
但这也不起作用,因为 waitDialog 不会立即显示,而是在操作完成之后才显示(当他们显示 joption 窗格“您以...登录”时)
我也尝试使用 invokeAndWait 而不是 invokeLater 但在这种情况下它会引发异常:
Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
我该怎么办?
【问题讨论】:
【参考方案1】:上述答案的变体
这是一种简单且可复制的方式...
//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();
【讨论】:
【参考方案2】:考虑使用 SwingWorker 执行后台工作,然后在 SwingWorker 的 done()
方法或(我的偏好)在添加到 SwingWorker 的 PropertyChangeListener 中关闭对话框。
例如,
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);
注意事项:
一个关键概念是设置所有内容,添加 PropertyChangeListener,让 SwingWorker 运行,所有在显示模态对话框之前,因为一旦显示模态对话框,调用代码的所有代码流都被冻结(如您所见)。 为什么我更喜欢 PropertyChangeListener 而不是使用 done 方法(正如 Elias 在他在这里的体面回答中所展示的那样,我对此表示赞同) - 使用侦听器提供了更多的关注点分离和更松散的耦合。这样,SwingWorker 就不必知道使用它的 GUI 代码。【讨论】:
它似乎有效,谢谢(也感谢@Elias):)。但现在还有一个小问题。当我关闭主应用程序(使用 dispose() )时,SwingWorker 线程继续运行。我必须明确关闭它吗?到达doInBackground的return语句时不终止吗? 如果execute
在dialog.pack()
之前结束怎么办?也许应用程序可能会卡住?
@Hovercraft... 我确实对其进行了测试,似乎dispose()
等到setVisible(true)
被调用,因此没关系。
@VitBernatik 你能否澄清dispose()
是否保证等到setVisible(true)
被调用,如果是这样,记录在哪里?我在 docs.oracle.com/javase/7/docs/api/java/awt/… 中找不到任何内容
@beldaz:我只是在我的软件上进行测试。有一些长时间的延误。我不能保证它会在新版本中工作。【参考方案3】:
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();
【讨论】:
以上是关于制作一个显示“请稍候”JDialog 的摆动线程的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Winforms 中显示“正在加载...请稍候”消息以获取长加载表单?