与 swingworker 的对话是 chicken/egg

Dialog with swingworker is a chicken/egg

我试图通过不在主线程 (EDT) 上执行长时间任务来遵循 Java 最佳实践。所以我打算将 swingWorker 与模态对话框一起使用。这样,模态对话框会阻止用户做任何事情,直到该任务完成,并且我可以在该过程进行时更新对话框上的状态。

现在的问题是模式对话框不仅会阻塞用户,而且在调用 setVisible 后也不会阻塞用户

所以如果我这样做

dialog.setVisible(true);
new SwingWorkerTask().execute(); //This does not get called

如果我这样做

new SwingWorkerTask().execute();
dialog.setVisible(true); // Well what the point of setting visible after the fact.

那么如何在任务进行时阻止用户操作并显示对话框?

谢谢

您可以尝试 SwingUtilities.invokeLater and SwingUtilities.invokeAndWait 而不是 swingWorker。

此外,this topic 可能会有用。

如果你这样做,它只是一个chicken/egg。您可以在 EDT 上构造所有 Swing 对象,然后让您的 SwingWorker(或任何其他线程)通过指示 EDT 通过 SwingUtilities.invokeLater(Runnable).

执行它们来管理所有更新
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class RudeProgressBar extends JFrame {

    private JButton button;

    public RudeProgressBar() {
        setTitle("Rude Progress Bar");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        button = new JButton("Do teh work");
        add(button, BorderLayout.SOUTH);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JDialog dialog = new JDialog(RudeProgressBar.this, true);
                dialog.setTitle("Doing teh work");
                dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
                final JProgressBar progressBar = new JProgressBar(0, 100);
                dialog.setLayout(new BorderLayout());
                dialog.add(progressBar);
                dialog.setSize(100, 100);
                dialog.setLocationRelativeTo(RudeProgressBar.this);
                MyTask task = new MyTask(dialog);
                task.addPropertyChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        if ("progress".equals(evt.getPropertyName())) {
                            progressBar.setValue((Integer)evt.getNewValue());
                        }
                    }
                });
                task.execute();
            }
        });

        setSize(200, 200);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new RudeProgressBar().setVisible(true);
            }
        });
    }

    private class MyTask extends SwingWorker<Void, Void> {

        private final JDialog dialog;

        public MyTask(JDialog dialog) {
            this.dialog = dialog;
        }

        @Override
        protected Void doInBackground() throws Exception {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    dialog.setVisible(true);
                }
            });

            int progress = 0;
            for (int i = 0; i < 5; i++) {
                Thread.sleep(1000);
                setProgress(progress += 20);
            }            

            return null;
        }

        @Override
        protected void done() {
            dialog.setVisible(false);
            dialog.dispose();
        }
    }
}

如果您担心 invokeLater 实现(在 SwingWorker.doInBackground 内)可能会在 SwingWorker.done 之后执行,只需将 done 中的代码放入另一个 [=13] =].通过这样做,您可以将 Runnable 实现排队,以便 EDT 以特定顺序执行它们。即使从 EDT 本身调用此方法,也会发生排队。

请注意,如果您查看 SwingWorker 实现,您会发现它依赖于 javax.swing.Timer 来执行 done() 并且 Timer 本身调用 invokeLater,因此再次在 done 中调用它等于什么都不做。但是,如果您这样做就不会出错。