As Peter mentioned in his answer, you shouldn't relay on decreasing a number, since there are not guarantees that actionPerformed
is invoked right in every second. The below is a working example, which stops the timer on finishing (detailed and therefor code):
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test extends JFrame {
private JTextField text;
private Timer timer;
private JButton start;
public Test() {
super("Countdown timer");
text = new JTextField("2", 8);
start = new JButton("Start");
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent click) {
final long current = System.currentTimeMillis();
try {
final long limit = Integer.parseInt(text.getText().trim())* 1000; // X seconds
timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent event) {
long time = System.currentTimeMillis();
long passed = time - current;
long remaining = limit - passed;
if(remaining <= 0) {
text.setText("2");
timer.stop();
} else {
long seconds = remaining/1000;
long minutes = seconds/60;
long hours = minutes/60;
text.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds%60));
}
}
});
timer.start();
} catch(NumberFormatException nfe) {
// debug/report here
nfe.printStackTrace();
}
}});
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.add(text);
panel.add(new JLabel(" seconds"));
panel.add(start);
add(panel);
}
public static void main(String [] args) throws Exception {
Test frame = new Test();
frame.setDefaultCloseOperation(Test.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}