Java make GUI wait for a timer

2019-08-20 04:28发布

问题:

I simply want this program to wait for a timer. All I want is for the program to pause for two seconds. I want this program to do is display "Start," wait for two seconds until the timer has finished, then display "Start, Finished Waiting, Finished." How can I make this program wait for the timer to finish? I believe that it currently creates the timer in a separate thread, not pausing the main thread, so it displays,"Start, Finished" then waits for two seconds and then displays "Start, Finished, Finished Waiting." This is not the order that I want things to happen in, and I have looked all over for a simple timer example when running a GUI and have found none. Thank you for your help, here is the code:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.Timer;

public class GUI extends JFrame {

    private static final long serialVersionUID = 3560258176733156660L;

    public static void main(String[] args) {
        new GUI().setVisible(true);
    }

    private Timer timer;
    private JTextArea area;
    private String text;

    public GUI() {
        setLayout(null);
        setSize(500, 120);
        setTitle("Timer");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        text = "";
        area = new JTextArea(text);
        area.setBounds(0, 0, 500, 120);
        add(area);
        doThings();

    }
    public void doThings() {
        text += "Start, ";
        area.setText(text);
        // Want program to wait for two seconds
        waitForTwoSeconds();
        text += "Finished ";
        area.setText(text);

    }
    public void waitForTwoSeconds() {

        timer = new Timer(2000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                text += "Finished Waiting, ";
                area.setText(text);
                timer.stop();
            }
        });
        timer.start();
    }

}

回答1:

Take the code from after you call waitForTwoSeconds and place within the actionPerformed method...

public void doThings() {
    area.setText("Start, ");
    // Want program to wait for two seconds
    waitForTwoSeconds();

}

public void waitForTwoSeconds() {

    timer = new Timer(2000, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            area.append("Finished Waiting, ");
            area.append("Finished ");
            timer.stop();
        }
    });
    timer.setRepeats(false);
    timer.start();
}

This will cause Finished Waiting, Finished to be append to the JTextArea 2 seconds after you click the button...

You DO NOT want to perform any long running/blocking operations within the context of the Event Dispatching Thread, this WILL make it look like your program as hang, cause it has.

See, Concurrency in Swing and How to use Swing Timers for more details

Updated...

Swing (and most UIs) are event driven, that is, something happens and you respond to it. For instance, with the Timer, the timer tripped and you responded to the event. You can't block/wait within the Event Dispatching Thread, it will simply cause the UI to stop responding and painting, this is the way the framework works, you can learn to live with it or continue to be frustrated by it (remember, wanting something and getting it to work, are two different things)

There are, however, things you can do, the Timer is one example, another is the SwingWorker

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JTextArea ta;

        public TestPane() {
            setLayout(new BorderLayout());
            ta = new JTextArea(10, 20);
            JButton btn = new JButton("Make it so");
            add(new JScrollPane(ta));
            add(btn, BorderLayout.SOUTH);

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    btn.setEnabled(false);
                    ta.append("Start, ");
                    SwingWorker<String, String> worker = new SwingWorker<String, String>() {

                        @Override
                        protected String doInBackground() throws Exception {
                            Thread.sleep(2000);
                            publish("Finished waiting, ");
                            return null;
                        }

                        @Override
                        protected void process(List<String> chunks) {
                            for (String text : chunks) {
                                ta.append(text);
                            }
                        }

                        @Override
                        protected void done() {
                            ta.append("Finished");
                            btn.setEnabled(true);
                        }

                    };
                    worker.execute();
                }
            });
        }

    }

}

What this basically does is, in a background thread, it waits two seconds and then (via the publish/process methods), prints "Finished Waiting", then after the doInBackground returns, done is (eventually) called and "Finished" is printed.

This is all done so that the UI updates occur from within the context of the Event Dispatching Thread, meeting the single thread requirements of Swing



回答2:

Ok, so I guess my question should really look like this then:
How do i make the program wait until the waitForTwoSeconds() method is complete before doing area.append("Finished "); ? This is really what I want to accomplish.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.Timer;

public class GUI extends JFrame {

    private static final long serialVersionUID = 3560258176733156660L;

    public static void main(String[] args) {
        new GUI().setVisible(true);
    }

    private Timer timer;
    private JTextArea area;

    public GUI() {
        setLayout(null);
        setSize(500, 120);
        setTitle("Timer");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        area = new JTextArea("");
        area.setBounds(0, 0, 500, 120);
        add(area);
        doThings();

    }

    public void doThings() {
        area.setText("Start, ");
        // Want program to wait for two seconds
        waitForTwoSeconds();
        // Don't want to do this until waitForTwoSeconds() has finished...
        area.append("Finished ");

    }

    public void waitForTwoSeconds() {

        timer = new Timer(2000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                area.append("Finished Waiting, ");
                timer.stop();
            }
        });
        timer.setRepeats(false);
        timer.start();
    }

}