H.Patel
H.Patel

Reputation: 11

Timer in javafx application

I'm trying to display a timer which counts and displays seconds in a label after clicking the start button but I'm not getting desired output.Here's my code of the controller and FXML file...

public class FXMLDocumentController implements Initializable {

    @FXML
    private static final Integer STARTTIME = 0;
    private Timeline timeline;
    private Label timerLabel = new Label();
    private IntegerProperty timeSeconds = new SimpleIntegerProperty(STARTTIME);
    Button button = new Button();


    public void handle(ActionEvent event) {
        if (timeline != null) {
            timeline.stop();
        }
        timeSeconds.set(STARTTIME);
        timeline = new Timeline();

        timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(STARTTIME+1),
                                    new KeyValue(timeSeconds, 0)));
        timeline.playFromStart();
        timerLabel.textProperty().bind(timeSeconds.asString());

    }



     public void initialize(URL url, ResourceBundle rb) {
         // TODO

     }    

}

and this is my FXML code..

<Button fx:id="button" layoutX="120.0" layoutY="40.0" 
        mnemonicParsing="false" text="Start Timmer" onAction="#handle" />
<Label layoutX="132.0" layoutY="92.0" prefHeight="17.0" prefWidth="65.0" 
       textFill="#f20b0b">
    <font>
        <Font size="24.0" />
    </font>
</Label>

Upvotes: 1

Views: 3050

Answers (1)

fabian
fabian

Reputation: 82491

You don't use Timeline correctly.

The duration of the KeyFrame is the offset from the start of the animation when the update is executed. Multiple clicks on your button all add KeyFrames that run 1 second after the start of the animation.

Furthermore using KeyValue interpolates the value from the current value to the value passed to KeyValue. This makes little sense, if the new value is the same as the old value.

You should instead use a EventHandler<ActionEvent> to update the property.

Furthermore the button field should be injected by FXMLLoader. This requires the field to be public or annotated with @FXML.

<Button fx:id="button" layoutX="120.0" layoutY="40.0" 
        mnemonicParsing="false" text="Start Timmer" onAction="#handle" />
<Label fx:id="timerLabel" layoutX="132.0" layoutY="92.0" prefHeight="17.0" prefWidth="65.0" 
       textFill="#f20b0b">
    <font>
        <Font size="24.0" />
    </font>
</Label>
public class FXMLDocumentController implements Initializable {

    private static final int STARTTIME = 0;
    private Timeline timeline;
    private final IntegerProperty timeSeconds = new SimpleIntegerProperty(STARTTIME);

    @FXML
    private Label timerLabel;
    @FXML
    private Button button;

    private void updateTime() {
        // increment seconds
        int seconds = timeSeconds.get();
        timeSeconds.set(seconds+1);
    }

    public void handle(ActionEvent event) {
        button.setDisable(true); // prevent starting multiple times
        timeline = new Timeline(new KeyFrame(Duration.seconds(1), evt -> updateTime())); 
        timeline.setCycleCount(Animation.INDEFINITE); // repeat over and over again
        timeSeconds.set(STARTTIME);
        timeline.play();
    }

    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        timerLabel.textProperty().bind(timeSeconds.asString());
    }    

}

Upvotes: 3

Related Questions