Dialog with swingworker is a chicken/egg

后端 未结 2 591
無奈伤痛
無奈伤痛 2021-01-24 06:25

I am trying to follow the Java best practices by not doing long tasks on the main thread (EDT). So I am planning to use a swingWorker with Modal Dialog. This way the modal dialo

相关标签:
2条回答
  • 2021-01-24 06:44

    It is only a chicken/egg if you make it such. You can construct all Swing objects on EDT and then let your SwingWorker (or any other thread) govern all updates by instructing EDT to execute them via 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();
            }
        }
    }
    

    If you are worried that the invokeLater implementation (inside SwingWorker.doInBackground) might get executed after SwingWorker.done, simply put the code in done into another invokeLater. By doing this, you queue your Runnable implementations for EDT to execute them in certain order. The queuing will happen even if this method is called from EDT itself.

    Note that if you take a look at SwingWorker implementation, you'll see that it relies on javax.swing.Timer to execute done() and the Timer itself calls invokeLater, so calling it inside done again amounts to doing nothing. Nothing will be wrong if you do it, however.

    0 讨论(0)
  • 2021-01-24 06:47

    You can try SwingUtilities.invokeLater and SwingUtilities.invokeAndWait instead of swingWorker.

    Also, this topic may be useful.

    0 讨论(0)
提交回复
热议问题