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();
}
}
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
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();
}
}