小编典典

与swingworker的对话是一只鸡/蛋

java

我试图通过不在主线程(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.

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

谢谢


阅读 218

收藏
2020-11-26

共1个答案

小编典典

如果您这样做的话,那只是鸡/蛋。您可以在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放入另一个invokeLater。通过这样做,您可以将Runnable实现排入队列以让EDT以特定顺序执行它们。即使从EDT本身调用此方法,也会发生排队。

请注意,如果您看一下SwingWorker实现,您会看到它依赖于javax.swing.Timer执行done()Timer本身调用invokeLater,因此done再次在内部调用它无济于事。但是,如果这样做,不会有任何问题。

2020-11-26