Search code examples
javajavafxjavafx-8

JavaFX Live Time and Date


I am building currently an application using JavaFx with an extra feature that displays the current date and time in the top corner of the scene. Since I am new to JavaFX, I don't know how to implement this one.

I tried to use an old code in swing but I got an IllegalStateException Error.

Here's my code.

MainMenuController.java

@FXML private Label time;

private int minute;
private int hour;
private int second;

@FXML
public void initialize() {

    Thread clock = new Thread() {
        public void run() {
            for (;;) {
                DateFormat dateFormat = new SimpleDateFormat("hh:mm a");
                Calendar cal = Calendar.getInstance();

                second = cal.get(Calendar.SECOND);
                minute = cal.get(Calendar.MINUTE);
                hour = cal.get(Calendar.HOUR);
                //System.out.println(hour + ":" + (minute) + ":" + second);
                time.setText(hour + ":" + (minute) + ":" + second);

                try {
                    sleep(1000);
                } catch (InterruptedException ex) {
                     //...
                }
            }
        }
    };
    clock.start();
}

MainMenu.fxml

 <children>
   <Label fx:id="time" textFill="WHITE">
     <font>
       <Font name="Segoe UI Black" size="27.0" />
     </font>
   </Label>
   <Label fx:id="date" textFill="WHITE">
     <font>
       <Font name="Segoe UI Semibold" size="19.0" />
     </font>
   </Label>
 </children>

Main.java

public class Main extends Application {

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

   @Override
   public void start(Stage primaryStage) throws Exception {
       Parent root = FXMLLoader.load(getClass().getResource("view/MainMenu.fxml"));
       primaryStage.setScene(new Scene(root,1366, 768));
       primaryStage.show();
   }
}

As you notice, I tested it printing the live time in the console. Yeah it worked, but the label is still static.


Solution

  • I think you need FX UI Thread Platform.runLater(...) for that, but you can do something like this using Timeline in you controller class,

    @FXML
    public void initialize() {
    
        Timeline clock = new Timeline(new KeyFrame(Duration.ZERO, e ->  
             time.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
        ),
             new KeyFrame(Duration.seconds(1))
        );
        clock.setCycleCount(Animation.INDEFINITE);
        clock.play();
    }
    

    Alternative solution using AnimationTimer - suggested by @James_D,

    AnimationTimer timer = new AnimationTimer() {
        @Override
        public void handle(long now) {
            time.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
    };
    timer.start();
    

    The second approach using AnimationTimer seems much cleaner and more accurate than with Timeline.


    You can check the POC of both approaches here.