Home > Back-end >  How to make a pause-able count up timer in Java?
How to make a pause-able count up timer in Java?

Time:12-07

I'm trying to make a timer that will count up from 00h:00m:00s, with the ability to pause and restart the count from its current time.

Heres the solution I currently have: It will always restart the timer from 0 instead of continuing where it left off. Also, for another inexplicable reason, the hours always display as 07 instead of 00. Does anyone know how I could fix these issues, to have it start counting up from its previous value, and display the correct amount of hours elapsed?

private final SimpleDateFormat date = new SimpleDateFormat("KK:mm:ss");
private long startTime = 0;
private final ClockListener clock = new ClockListener();
private final Timer normalTimer = new Timer(53, clock);
startTimerButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(startTime == 0) {
                    startTime = System.currentTimeMillis();
                }
                else {
                    startTime  = (System.currentTimeMillis() - startTime);
                }
                normalTimer.start();
            }
        });
        
        stopTimerButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                normalTimer.stop();
            }
        });

private void updateClock(){
     Date elapsed = new Date(System.currentTimeMillis() - startTime);
     timerText.setText(date.format(elapsed));
}
private class ClockListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            updateClock();
        }
    }

CodePudding user response:

Introduction

Oracle has a helpful tutorial, Start

When you press the "Start" button, the timer starts counting. 37 seconds.

Seconds

2 minutes, 7 seconds.

Minutes

1 hour, 6 minutes, 11 seconds.

Hours

Pressing the "Pause button pauses the count. Pressing the "Restart" button resumes the count. You can pause and resume the count as many times as you want.

Pressing the "Stop" button stops the counter. You can press the "Reset" button to reset the counter before starting again.

Explanation

When creating a Swing application, using the model-view-controller (MVC) pattern helps to separate your concerns and allows you to focus on one part of the application at a time.

Creating the application model made creating the GUI much easier. The application model is made up of one or more plain Java getter/setter classes.

The CountupTimerModel class keeps long fields to hold the duration in milliseconds and the previous duration. This way, I don't have to pause and restart the Swing Timer.

The duration is the difference between the current time and the start time. I use the System.currentTimeMillis() method to calculate the duration.

The GUI is fairly straightforward.

I made the JButton ActionListeners lambdas. I made the TimerListener a separate class to move the code out of that particular lambda.

Code

Here's the complete runnable code. I made the additional classes inner classes so I could post the code as one block.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class CountupTimerGUI implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new CountupTimerGUI());
    }

    private final CountupTimerModel model;

    private JButton resetButton, pauseButton, stopButton;

    private JLabel timerLabel;

    public CountupTimerGUI() {
        this.model = new CountupTimerModel();
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Countup Timer GUI");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(createDisplayPanel(), BorderLayout.NORTH);
        frame.add(createButtonPanel(), BorderLayout.SOUTH);

        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createDisplayPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
        Font font = panel.getFont().deriveFont(Font.BOLD, 48f);

        timerLabel = new JLabel(model.getFormattedDuration());
        timerLabel.setFont(font);
        panel.add(timerLabel);

        return panel;
    }

    private JPanel createButtonPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
        Font font = panel.getFont().deriveFont(Font.PLAIN, 16f);

        resetButton = new JButton("Reset");
        resetButton.setFont(font);
        panel.add(resetButton);
        resetButton.addActionListener(event -> {
            model.resetDuration();
            timerLabel.setText(model.getFormattedDuration());
        });

        pauseButton = new JButton("Restart");
        pauseButton.setFont(font);
        panel.add(pauseButton);
        pauseButton.addActionListener(event -> {
            String text = pauseButton.getText();
            if (text.equals("Pause")) {
                model.pauseTimer();
                pauseButton.setText("Restart");
            } else {
                model.startTimer();
                pauseButton.setText("Pause");
            }
        });

        Timer timer = new Timer(200,
                new CountupListener(CountupTimerGUI.this, model));
        stopButton = new JButton("Start");
        stopButton.setFont(font);
        panel.add(stopButton);
        stopButton.addActionListener(event -> {
            String text = stopButton.getText();
            if (text.equals("Start")) {
                model.resetDuration();
                model.startTimer();
                timer.start();
                resetButton.setEnabled(false);
                stopButton.setText("Stop");
            } else {
                model.stopTimer();
                timer.stop();
                resetButton.setEnabled(true);
                stopButton.setText("Start");
            }
        });

        Dimension d = getLargestJButton(resetButton, pauseButton, stopButton);
        resetButton.setPreferredSize(d);
        pauseButton.setPreferredSize(d);
        stopButton.setPreferredSize(d);
        pauseButton.setText("Pause");

        return panel;
    }

    private Dimension getLargestJButton(JButton... buttons) {
        Dimension largestDimension = new Dimension(0, 0);
        for (JButton button : buttons) {
            Dimension d = button.getPreferredSize();
            largestDimension.width = Math.max(largestDimension.width, d.width);
            largestDimension.height = Math.max(largestDimension.height,
                    d.height);
        }

        largestDimension.width  = 10;
        return largestDimension;
    }

    public JButton getResetButton() {
        return resetButton;
    }

    public JButton getPauseButton() {
        return pauseButton;
    }

    public JButton getStopButton() {
        return stopButton;
    }

    public JLabel getTimerLabel() {
        return timerLabel;
    }

    public class CountupListener implements ActionListener {

        private final CountupTimerGUI view;

        private final CountupTimerModel model;

        public CountupListener(CountupTimerGUI view, CountupTimerModel model) {
            this.view = view;
            this.model = model;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            model.setDuration();
            view.getTimerLabel().setText(model.getFormattedDuration());
        }

    }

    public class CountupTimerModel {

        private boolean isRunning;

        private long duration, previousDuration, startTime;

        public CountupTimerModel() {
            resetDuration();
        }

        public void resetDuration() {
            this.duration = 0L;
            this.previousDuration = 0L;
            this.isRunning = true;
        }

        public void startTimer() {
            this.startTime = System.currentTimeMillis();
            this.isRunning = true;
        }

        public void pauseTimer() {
            setDuration();
            this.previousDuration = duration;
            this.isRunning = false;
        }

        public void stopTimer() {
            setDuration();
            this.isRunning = false;
        }

        public void setDuration() {
            if (isRunning) {
                this.duration = System.currentTimeMillis() - startTime
                          previousDuration;
            }
        }

        public String getFormattedDuration() {
            int seconds = (int) ((duration   500L) / 1000L);
            int minutes = seconds / 60;
            int hours = minutes / 60;

            StringBuilder builder = new StringBuilder();
            if (hours > 0) {
                builder.append(hours);
                builder.append(":");
            }

            minutes %= 60;
            if (hours > 0) {
                builder.append(String.format("d", minutes));
                builder.append(":");
            } else if (minutes > 0) {
                builder.append(minutes);
                builder.append(":");
            }

            seconds %= 60;
            if (hours > 0 || minutes > 0) {
                builder.append(String.format("d", seconds));
            } else {
                builder.append(seconds);
            }

            return builder.toString();
        }
    }

}
  • Related