0
votes

I have programmed a simple stopwatch with three functionalities. First, I have a start button to begin the stopwatch , a pause button to pause the stopwatch and finally a reset button to reset the entire stopwatch.

When I hit the pause button, the stopwatch pauses, say at 10.0 seconds. When I resume the stopwatch (pressing the Start button again), the stopwatch doesn't resume from 10.0 seconds onwards. It resumes from the amount of time I paused and the current time. For example, if I paused for 5 seconds and hit resume, the stopwatch goes from 15.0 seconds onwards.

I am aware there isn't a actual pause function in Swing.Timer. Would there be a way to tackle this so the stopwatch resumes normally?

Any suggestion would be appreciated.

Code:

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import javax.swing.Timer;

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



public class GuiStopwatch {





    public static void main(String[] args) {
        JFrame frame = new JFrame("Stopwatch");

        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
        frame.setVisible(true);
          JPanel panel = new JPanel();

          panel.setLayout(null);

          JButton startbtn = new JButton("START");  
          JButton pausebtn = new JButton("PAUSE");
          JButton reset = new JButton("RESET");
          JLabel time = new JLabel("Time shows here");
             panel.add(startbtn);
             panel.add(pausebtn);
            panel.add(reset);
            panel.add(time);
             startbtn.setBounds(50, 150, 100, 35);
             pausebtn.setBounds(50, 200, 100, 35);
             reset.setBounds(50, 250, 100, 35);
             time.setBounds(50, 350, 100, 35);
             time.setBackground(Color.black);
             time.setForeground(Color.red);
             frame.add(panel);



            Timer timer = new Timer(1,new ActionListener() {
                Instant start = Instant.now();
                @Override
                public void actionPerformed(ActionEvent e) {


                    time.setText( Duration.between(start, Instant.now()).getSeconds() +  ":" + Duration.between(start, Instant.now()).getNano() );


                }
            });



             startbtn.addActionListener(new ActionListener() {


                @Override
                public void actionPerformed(ActionEvent e) {





                timer.start();




                }
            });



             pausebtn.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {

                    timer.stop();

                }
            });



             reset.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                time.setText("0:0");

                }
            });
1
So, the "basic" concept is - you need to keep track the total ruining time of the stop watch, that is the time from when it was first started to when it was paused. When resumed, you would then add the "new running" time to the previous tally - MadProgrammer
panel.setLayout(null); <— avoid this. - achAmháin

1 Answers

3
votes

Conceptually, the idea is, you want to keep track of the "total running" of the stop watch, this is, all the total duration it has been active.

There's a number of ways you might achieve this, one might be to simply keep a running total which is only updated when the stop watch is stopped or paused. The "duration" of the stop watch is then a sum of the "current duration" of the "current" cycle and the "total previous" duration

Something like...

public class StopWatch {
    private LocalDateTime startTime;
    private Duration totalRunTime = Duration.ZERO;

    public void start() {
        startTime = LocalDateTime.now();
    }

    public void stop() {
        Duration runTime = Duration.between(startTime, LocalDateTime.now());
        totalRunTime = totalRunTime.plus(runTime);
        startTime = null;
    }

    public void pause() {
        stop();
    }

    public void resume() {
        start();
    }

    public void reset() {
        stop();
        totalRunTime = Duration.ZERO;
    }

    public boolean isRunning() {
        return startTime != null;
    }

    public Duration getDuration() {
        Duration currentDuration = Duration.ZERO;
        currentDuration = currentDuration.plus(totalRunTime);
        if (isRunning()) {
            Duration runTime = Duration.between(startTime, LocalDateTime.now());
            currentDuration = currentDuration.plus(runTime);
        }
        return currentDuration;
    }
}

Okay, so start and stop are essentially the same as pause and resume, but you get the point.

And, a runnable example...

Now, this example runs a Swing Timer constantly, but the StopWatch can paused and resumed at any time, the point is to demonstrate that the StopWatch is actually working correctly ;)

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.LocalDateTime;
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 Test {

    public static void main(String[] args) throws InterruptedException {
        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 JLabel label;
        private JButton btn;

        private StopWatch stopWatch = new StopWatch();
        private Timer timer;

        public TestPane() {
            label = new JLabel("...");
            btn = new JButton("Start");

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(label, gbc);
            add(btn, gbc);

            timer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    label.setText(Long.toString(stopWatch.getDuration().getSeconds()));
                }
            });
            timer.start();

            btn.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (stopWatch.isRunning()) {
                        stopWatch.pause();
                        btn.setText("Start");
                    } else {
                        stopWatch.resume();
                        btn.setText("Pause");
                    }
                }
            });

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.dispose();
        }

    }

    public class StopWatch {
        private LocalDateTime startTime;
        private Duration totalRunTime = Duration.ZERO;

        public void start() {
            startTime = LocalDateTime.now();
        }

        public void stop() {
            Duration runTime = Duration.between(startTime, LocalDateTime.now());
            totalRunTime = totalRunTime.plus(runTime);
            startTime = null;
        }

        public void pause() {
            stop();
        }

        public void resume() {
            start();
        }

        public void reset() {
            stop();
            totalRunTime = Duration.ZERO;
        }

        public boolean isRunning() {
            return startTime != null;
        }

        public Duration getDuration() {
            Duration currentDuration = Duration.ZERO;
            currentDuration = currentDuration.plus(totalRunTime);
            if (isRunning()) {
                Duration runTime = Duration.between(startTime, LocalDateTime.now());
                currentDuration = currentDuration.plus(runTime);
            }
            return currentDuration;
        }
    }

}