Create a count up timer in java (seconds and milliseconds only)

19,478

You are counting and relying on the timer to expire in exactly 1 millisecond. The timer (actually the OS) doesn't guarantee that exactly and only 1 millisecond will happen before the expiration, and the code you are running upon expiration takes some time too. Instead, use the timer just to trigger the refresh. I chose 53ms, as it gives the user a fuzzy feeling that the milliseconds are flying by, but notice that the timer updates 1 final time after the user clicks stop, which does not have to be at a multiple of 53ms. The time that gets displayed is not related to the number of Timer expirations, only to the start time that was recorded when the user pressed Start and the current System time:

public class Counter extends JFrame {

    private static final String stop = "Stop";
    private static final String start = "Start";
    private final ClockListener clock = new ClockListener();
    private final Timer timer = new Timer(53, clock);
    private final JTextField tf = new JTextField(9);
    private final SimpleDateFormat date = new SimpleDateFormat("mm.ss.SSS");
    private long startTime;

    public Counter() {
        timer.setInitialDelay(0);

        JPanel panel = new JPanel();
        tf.setHorizontalAlignment(JTextField.RIGHT);
        tf.setEditable(false);
        panel.add(tf);
        final JToggleButton b = new JToggleButton(start);
        b.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                if (b.isSelected()) {
                    startTime = System.currentTimeMillis();
                    timer.start();
                    b.setText(stop);
                } 
                else {
                    updateClock();
                    startTime = 0;

                    timer.stop();
                    b.setText(start);
                }
            }
        });
        panel.add(b);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(panel);
        this.setTitle("Timer");
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
    private void updateClock() {
        Date elapsed = new Date(System.currentTimeMillis() - startTime);
        tf.setText(date.format(elapsed));
    }
    private class ClockListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            updateClock();
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                Counter clock = new Counter();
            }
        });
    }
}
Share:
19,478
user2144555
Author by

user2144555

Updated on June 05, 2022

Comments

  • user2144555
    user2144555 almost 2 years

    I'm doing a count up timer in java. I found the following example: Creating a Count Up timer to Break in java, and that's working fine. But I want to show the time in the format "mm:ss:SSS", because I want to measure really small time responses. So, when it reaches the 1000 msec, it is 1 second.

    I made some changes, but I can't present the time in the pretended format. The numbers start to appear were the seconds should be, and not in the milliseconds.

    If you have a better example that the one that I'm following, it's fine.

    Edit: It is better, but it is not working fine yet. It is counting too slow (1 minute here correspond to 2 real minutes). My code is here:

    public class Counter extends JFrame {
    
        private static final String stop = "Stop";
        private static final String start = "Start";
        private final ClockListener clock = new ClockListener();
        private final Timer timer = new Timer(1, clock);
        private final JTextField tf = new JTextField(9);
    
    public Counter() 
    {
        timer.setInitialDelay(0);
    
        JPanel panel = new JPanel();
        tf.setHorizontalAlignment(JTextField.RIGHT);
        tf.setEditable(false);
        panel.add(tf);
        final JToggleButton b = new JToggleButton(start);
        b.addItemListener(new ItemListener() 
        {
            @Override
            public void itemStateChanged(ItemEvent e) 
            {
                if (b.isSelected()) 
                {
                    timer.start();
                    b.setText(stop);
                } 
                else 
                {
                    timer.stop();
                    b.setText(start);
                }
            }
        });
        panel.add(b);
    
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(panel);
        this.setTitle("Timer");
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
    
    private class ClockListener implements ActionListener
    {
        private int minutes;
        private int seconds;
        private int milliseconds;
    
        @Override
        public void actionPerformed(ActionEvent e) 
        {
            SimpleDateFormat date = new SimpleDateFormat("mm.ss.SSS");
    
            if (milliseconds == 1000) 
            {
                milliseconds = 000;
                seconds++;
            }
            if (seconds == 60) {
                seconds = 00;
                minutes++;
            }
    
            tf.setText(String.valueOf(minutes + ":" + seconds + ":" + milliseconds));
            milliseconds++;
        }
    }
    
    public static void main(String[] args) 
    {
        EventQueue.invokeLater(new Runnable() 
        {
            @Override
            public void run() {
                Counter clock = new Counter();
                clock.start();
            }
        });
      }
    }
    
  • trashgod
    trashgod almost 11 years
    See also this related approach for slightly longer periods.