Search code examples
javaswingbuttontimerjavax.swing.timer

javax.swing.timer subtracts as much as I click on start button


First of all hi! This is my first post on stackoverflow! This is my second attempt to program something in Java and the first ever attempt with a gui.

I'm actually having 2 problems. The first being the program and the second understanding a part of the code.

How the program should work:

When pressing start it counts down from 01:00 to 00:00 every minute (01:00 -> 00:59 -> 00:58). When you press stop, it stops counting down (duh) and when you press start again, it starts from 01:00 like the first time.

The program problem:

With that said. This only works the first time I press start. When I press start multiple times it subtracts that amount of times from the clock. Pressed 2 times (01:00 -> 00:58 -> 00:56). Pressed 4 times (01:00 -> 00:56 -> 00:52). etc... This obviously should not be happening.

The understanding problem:

I am having a hard time understanding why the timer requires an ActionListener and why it works when you use 'null'. In some cases it also works when using 'this' (which I also don't understand.).

Java Swing Timer Documentation

import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class CountdownClock extends JFrame
{

    private int oneSecond = 1000; //Milliseconds
    private Timer timer = new Timer(oneSecond * 60, null);
    private int timerCount = 59;

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

    CountdownClock()
    {
        this.getContentPane().setLayout(null);
        this.setBounds(800, 450, 300, 125);

        final JLabel countdownLabel = new JLabel("01:00");
        countdownLabel.setBounds(110, 10, 125, 30);
        countdownLabel.setFont(new Font("Serif", Font.PLAIN, 30));

        JButton startButton = new JButton("Start");
        startButton.setBounds(10, 50, 125, 30);
        startButton.addActionListener(new ActionListener() 
        {
        public void actionPerformed(ActionEvent e)
        {
            timer.setRepeats(true);
            timer.stop();
            countdownLabel.setText("01:00");
            timerCount = 59;
            timer.start();
            timer.addActionListener(new ActionListener() 
            {
                public void actionPerformed(ActionEvent evt)
                {     
                    if (timerCount == 0)
                    {
                        timer.stop();
                        countdownLabel.setText("00:00");
                        timerCount = 59;
                    }
                    else if (timerCount <= 9)
                    {
                        countdownLabel.setText("00:0" + String.valueOf(timerCount));
                        timerCount = timerCount - 1;
                    }
                    else
                    {
                        countdownLabel.setText("00:" + String.valueOf(timerCount));
                        timerCount = timerCount - 1;
                    }
                }
            });
        }
    });

    JButton stopButton = new JButton("Stop");
    stopButton.setBounds(150, 50, 125, 30);
    stopButton.addActionListener(new ActionListener() 
    {
        public void actionPerformed(ActionEvent e)
        {
            timer.stop();
            countdownLabel.setText("01:00");
            timerCount = 59;
        }
    });

    add(countdownLabel);
    add(startButton);
    add(stopButton);

    setVisible(true);
    }
}

Solution

  • This happens because you are adding an ActionListener to the Timer every time that you press the button. So, since a Timer allows multiple listeners, everyone of them gets notified when the timer ticks.

    To solve the problems you could just instantiate a new Timer every time you press the start button (timer = new Timer()). Or add the ActionListener just once in your JFrame constructor . Or even remove the listener (but you should save a reference to it somewhere).