Search code examples
javajavafxreal-time-clock

What is the best way to synchronize a Timeline clock in JavaFX?


I am trying to display a synchronized clock in my Java FX GUI with the function given below.

This also includes a timer function which runs if Timer=true

private Calendar cal;
private int minute;
private int hour;
private int second;
private String am_pm;

private boolean Timer;
private Integer tseconds;
private void startClock() {
    Timeline clock = new Timeline(new KeyFrame(Duration.millis(Calendar.getInstance().get(Calendar.MILLISECOND)), new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {

            cal = Calendar.getInstance();
            second = cal.get(Calendar.SECOND);
            minute = cal.get(Calendar.MINUTE);
            hour = cal.get(Calendar.HOUR);
            am_pm = (cal.get(Calendar.AM_PM) == 0) ? "AM" : "PM";
            time.setText(String.format("%02d : %02d : %02d %s", hour, minute, second, am_pm));
            if (Timer) {
                if (tseconds == 0) {
                    Timer = false;
                    //timer.setText("Time Out");
                } else {
                    //timer.setText(tseconds.toString());
                    tseconds--;
                }
            }
        }
    }), new KeyFrame(Duration.millis(Calendar.getInstance().get(Calendar.MILLISECOND))));
    clock.setCycleCount(Animation.INDEFINITE);
    clock.play();
}

I tested this few times and found that many a times the clock updates at a variable speed.

Solved

Look for the solution below.


Solution

  • Solved the issue.

    I was setting the duration as the current milliseconds instead of milliseconds remaining for the next second.

    Only the first KeyFrame should have the duration (1000-current millisecond) as the KeyFrame will run for a duration which is equal to the milliseconds left in the current second.

    The following KeyFrames will be running at an interval of 1 second.

    private void startClock() {
        Timeline clock = new Timeline(new KeyFrame(Duration.millis(1000 - Calendar.getInstance().get(Calendar.MILLISECOND)), new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
    
                cal = Calendar.getInstance();
                second = cal.get(Calendar.SECOND);
                minute = cal.get(Calendar.MINUTE);
                hour = cal.get(Calendar.HOUR);
                am_pm = (cal.get(Calendar.AM_PM) == 0) ? "AM" : "PM";
                time.setText(String.format("%02d : %02d : %02d %s", hour, minute, second, am_pm));
                if (Timer) {
                    if (tseconds == 0) {
                        Timer = false;
                        //timer.setText("Time Out");
                    } else {
                        //timer.setText(tseconds.toString());
                        tseconds--;
                    }
                }
            }
        }), new KeyFrame(Duration.seconds(1)));
        clock.setCycleCount(Animation.INDEFINITE);
        clock.play();
    }