8

I'm making a timer in Java and I need help. There is a swing timer added to the main class. I have a jFrame which has 2 panels, 1 having the jLabel, the other one having 3 buttons, "Start", "Stop" and "Reset". When I click start then everything is working fine and when I stop then reset. But when I click on start again, it pumps out this exception:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Timer already                                       cancelled.
at java.util.Timer.sched(Unknown Source)
at java.util.Timer.schedule(Unknown Source)
at org.stopwatch.Stopwatch.start(Stopwatch.java:71)
at org.stopwatch.Stopwatch$1.actionPerformed(Stopwatch.java:48)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

This is my code: package org.stopwatch;

import static javax.swing.UIManager.getSystemLookAndFeelClassName;
import static javax.swing.UIManager.setLookAndFeel;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Stopwatch {

private static boolean running = false;
public static int time = 0;
public static Timer timer = new Timer();
public static JLabel leftLabel = new JLabel(time + "s");

public static final void main(String[] args) throws Exception {

    setLookAndFeel(getSystemLookAndFeelClassName());
    JFrame f = new JFrame();
    f.setVisible(true);
    f.setSize(1, 1);
    f.setTitle("Секундомір");
    f.setDefaultCloseOperation(3);
    f.setLocationRelativeTo(null);
    JPanel leftPanel = new JPanel();
    f.add(leftPanel, BorderLayout.NORTH);
    leftPanel.add(leftLabel);
    JPanel buttonPanel = new JPanel();
    f.add(buttonPanel, BorderLayout.SOUTH);
    JButton startBtn = new JButton("Start");
    JButton stopBtn = new JButton("Stop");
    JButton resetBtn = new JButton("Reset");
    buttonPanel.add(startBtn);
    buttonPanel.add(stopBtn);
    buttonPanel.add(resetBtn);
    startBtn.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent a) {
            running = true;
            start();
        }
    });
    stopBtn.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent a) {
            running = false;
            timer.cancel();
        }
    });
    resetBtn.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent a) {
            if(!running) {
                time = 0;
                leftLabel.setText("0s");
            }
        }
    });
    f.pack();
}

public static void start() {
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            time++;
            leftLabel.setText(time + "s");
        }
    }, 1000, 1000);
}

}
Jack Bracken
  • 1,233
  • 12
  • 23
mid
  • 421
  • 5
  • 20

4 Answers4

12

The answer is in the error message. It says you can't start a timer that has been cancelled. You will have to create a new Timer using the new operator.

Do this in your start() function.

timer = new Timer();

If you decide to go this route, don't forget to remove the new operation from your declaration. You will overwrite the assignment in your start() function.

public static Timer timer;

If you want to store the time between runs, you need to do that before you overwrite the timer with a new one.

Rainbolt
  • 3,542
  • 1
  • 20
  • 44
  • Thanks. i renewed the timer in the start method. Its working now! – mid Feb 10 '14 at 16:18
  • @user2564491 Why are you using a `java.util.Timer`? You should be using `javax.swing.Timer` for Swing apps, as seen [**here**](http://stackoverflow.com/a/21619240/2587435) – Paul Samsotha Feb 10 '14 at 16:28
  • 1
    @peeskillet If `java.util.Timer` satisfies his needs, he should definitely use it. If he needed his timer to update his GUI, then he should use `javax.swing.Timer`. However, the opposite is true - his GUI is updating his timer, in which case there is no reason to use the swing timer. – Rainbolt Feb 10 '14 at 16:42
  • 1
    @peeskillet You seem to be confusing _updating the GUI_ with _displaying the timer in the GUI_. His timer isn't firing off events, showing and hiding elements, and doing other GUI manipulations. Displaying a time is not the same as firing events on a timer. Just so we are clear, `setText()` is a function of the Label, NOT the timer. If that isn't clear enough, imagine you wanted to display an integer in your GUI. Would you use some swing variable for that? – Rainbolt Feb 10 '14 at 17:15
4

Yes, it's behaving exactly as documented. As per the cancel() documentation:

Once a timer has been terminated, its execution thread terminates gracefully, and no more tasks may be scheduled on it.

And as per the documentation of the schedule methods:

IllegalStateException - if task was already scheduled or cancelled, timer was cancelled, or timer thread terminated.

Basically, you'll need to create a new Timer on each start operation - or just don't cancel the existing one, changing the run method of your TimerTask to ignore operations when the timer is logically stopped.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
0

You should make a new timer on start() and fill it with the time from the 'old' timer.

Take a look at this

Community
  • 1
  • 1
0

When you call timer.cancel(), then the timer thread will be stop! you may stop some task, but not the timer! when the timer is canceled,you have to new a tiemr!

you can look this

easynoder
  • 104
  • 4