'Swing Timer stopwatch in Java

Can someone provide me an example of a Swing Timer stopwatch GUI in Java using a constantly-updating JLabel? I am not familiar with using @Override, so please don't suggest code with that in it unless it is absolutely necessary (I've done other Swing Timers, such as a system clock, without it).

Thanks!

EDIT: As per @VGR's request, here's the code I have for my basic clock that uses a Swing Timer:

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.FlowLayout;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.Font;

public class basic_clock extends JFrame
{
    JLabel date, time;

    public basic_clock()
    {
        super("clock");

        ActionListener listener = new ActionListener()
        {
            public void actionPerformed(ActionEvent event)
            {
                Calendar current = Calendar.getInstance();
                current.setTime(new Date());
                date.setText((current.get(Calendar.MONTH) + 1) +"/" +current.get(Calendar.DATE) +"/" +current.get(Calendar.YEAR));
                String timeStr = String.format("%d:%02d:%02d", current.get(Calendar.HOUR), current.get(Calendar.MINUTE), current.get(Calendar.SECOND));
                time.setText(timeStr);                
            }
        };

        date = new JLabel();
        time = new JLabel();

        setLayout(new FlowLayout());
        setSize(310,190);
        setResizable(false);
        setVisible(true);

        add(date);
        add(time);

        date.setFont(new Font("Arial", Font.BOLD, 64));
        time.setFont(new Font("Arial", Font.BOLD, 64));

        javax.swing.Timer timer = new javax.swing.Timer(500, listener);
        timer.setInitialDelay(0);
        timer.start();
    }

    public static void main(String args[])
    {

        basic_clock c = new basic_clock();
        c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Obviously I need something different than the Calendar object since I want to keep track of minutes and seconds to the nearest 1/100th of a second instead of date/month/year/hour/minute/second.



Solution 1:[1]

I did Google for it, but I wasn't understanding the code that I found

Then you have a bigger problem. How do you expect any of us to provide you with an example you can understand?

A stop watch is conceptually pretty simple, it's simply the amount of time that has passed since it was started. Problems arise when you want to be able to pause the timer, as you need to take into account the amount of time the timer has been running plus the time since it was last started/resumed.

Another issue is, most timers only guarantee a minimum amount of time, so they are imprecise. This means you can't just keep adding the amount of the timer's delay to some variable, you'll eventually end up with a drifting value (inaccurate)

This is a very simple example, all it does is provides a start and stop button. Each time the stop watch is started, it starts from 0 again.

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SimpleStopWatch {

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

    public SimpleStopWatch() {
        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 StopWatchPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class StopWatchPane extends JPanel {

        private JLabel label;
        private long lastTickTime;
        private Timer timer;

        public StopWatchPane() {
            setLayout(new GridBagLayout());
            label = new JLabel(String.format("%04d:%02d:%02d.%03d", 0, 0, 0, 0));

            timer = new Timer(100, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    long runningTime = System.currentTimeMillis() - lastTickTime;
                    Duration duration = Duration.ofMillis(runningTime);
                    long hours = duration.toHours();
                    duration = duration.minusHours(hours);
                    long minutes = duration.toMinutes();
                    duration = duration.minusMinutes(minutes);
                    long millis = duration.toMillis();
                    long seconds = millis / 1000;
                    millis -= (seconds * 1000);
                    label.setText(String.format("%04d:%02d:%02d.%03d", hours, minutes, seconds, millis));
                }
            });

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);
            add(label, gbc);

            JButton start = new JButton("Start");
            start.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!timer.isRunning()) {
                        lastTickTime = System.currentTimeMillis();
                        timer.start();
                    }
                }
            });
            JButton stop = new JButton("Stop");
            stop.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    timer.stop();
                }
            });

            gbc.gridx = 0;
            gbc.gridy++;
            gbc.weightx = 0;
            gbc.gridwidth = 1;
            add(start, gbc);
            gbc.gridx++;
            add(stop, gbc);
        }

    }

}

Adding a pause feature isn't hard, it would simply require one additional variable, but I'll leave that up to you to figure out.

Solution 2:[2]

For those like myself who would prefer a stopwatch focused in sports, the idea would be to have a sound when clicking on the start button as well as "Take your marks" button with its corresponding sound. For example, in the Olympic swimming competitions videos I recorded the "Take your marks" and the Beep sound at starts (enough of yelling GO! during practice). Using MadProgrammer's answer, you will need the tiny Jaco lib to play the mp3 files you want. Implementing this in an Android app is the way to go.

Edit: this was supposed to be a comment but I still have no reputation for that, therefore, I wanted to share this idea for a different use. I also added a reset button.

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import java.io.File;
import jaco.mp3.player.MP3Player;


public class TimerStopwatch {

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

    public TimerStopwatch() {
        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 StopWatchPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class StopWatchPane extends JPanel {

        private JLabel label;
        private long lastTickTime;
        private Timer timer;

        public StopWatchPane() {
            setLayout(new GridBagLayout());
            label = new JLabel(String.format("%02d:%02d:%02d.%03d", 0, 0, 0, 0));

            timer = new Timer(100, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    long runningTime = System.currentTimeMillis() - lastTickTime;
                    Duration duration = Duration.ofMillis(runningTime);
                    long hours = duration.toHours();
                    duration = duration.minusHours(hours);
                    long minutes = duration.toMinutes();
                    duration = duration.minusMinutes(minutes);
                    long millis = duration.toMillis();
                    long seconds = millis / 1000;
                    millis -= (seconds * 1000);
                    label.setText(String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis));
                }
            });

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);
            add(label, gbc);

            JButton start = new JButton("Start");
            start.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!timer.isRunning()) {
                        lastTickTime = System.currentTimeMillis();
                        /* This will play the Beep or Fire sound of your choice */
                        new MP3Player(new File("Beep.mp3")).play();
                        timer.start();


                    }
                }
            });
            JButton stop = new JButton("Stop");
            stop.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    timer.stop();
                }
            });
            
            /* This button will play the "Take your marks" recording of your choice  */
            JButton takeyourmarks = new JButton("Take your Marks");
            takeyourmarks.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    new MP3Player(new File("Marks1.mp3")).play();
                }
            });

            JButton reset = new JButton("Reset");
            reset.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    label.setText(String.format("%02d:%02d:%02d.%03d", 0, 0, 0, 0));
                }
            });

            gbc.gridx = 0;
            gbc.gridy++;
            gbc.weightx = 0;
            gbc.gridwidth = 1;
            add(takeyourmarks, gbc);
            gbc.gridx++;
            add(start, gbc);
            gbc.gridx++;
            add(stop, gbc);
            gbc.gridx++;
            add(reset, gbc);

        }

    }

}

Solution 3:[3]

I think you make: private long runningTime;

line

lastTickTime = System.currentTimeMillis();
edit ->  lastTickTime = System.currentTimeMillis() - runningTime ;

or specifically

start.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!timer.isRunning()) {
                    lastTickTime = System.currentTimeMillis() - runningTime ;
                    timer.start();
                }
            }
        });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 procrastinator