I am working in JavaFX right now, and have run into a problem. I created a simple example to demonstrate the issue I am facing.
My issue is, I have a Timeline
object setup on multiple anonymous Circle
objects and I want an action to happen after the Timeline has finished its play()
method. To do this, I setup a setOnFinished
event handler to execute something after the animation has finished playing, However, it is executing this logic multiple times because it is working on multiple objects.
Here I have a simple program that adds 3 anonymous objects to a VBox
and there is a button that will call the flash()
method to start a Timeline animation on the circles.
package sample;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
VBox root = new VBox();
VBox circles = new VBox();
Button btn = new Button("Touch me to make me flash ;)");
btn.setOnAction(e -> flash(circles));
for(int i = 0; i < 3; i++) {
circles.getChildren().add(new Circle(25, Color.RED));
}
circles.setSpacing(10);
circles.setAlignment(Pos.CENTER);
root.getChildren().addAll(circles, btn);
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 500, 500));
primaryStage.show();
}
private void flash(VBox root) {
for(Node circle : root.getChildren()) {
final Circle c = (Circle) circle;
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0.5), e -> c.setFill(Color.GOLD)),
new KeyFrame(Duration.seconds(1.0), e -> c.setFill(Color.RED))
);
timeline.setCycleCount(5);
timeline.play();
timeline.setOnFinished(e -> System.out.println("Do something here"));
}
}
}
You can see in the flash()
method that there is an EventHandler that executes this line of code:
System.out.println("Do something here")
I want it to only be executing one time, but it executes 3 times because the Timeline object is setup on 3 circles.
How can I make it so the EventHandler only executes one time?
Consider using one TimeLine
object for all Circle
objects:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private ObjectProperty<Paint> colorProperty;
@Override
public void start(Stage primaryStage) throws Exception{
colorProperty = new SimpleObjectProperty<>(Color.WHITE);
VBox root = new VBox();
VBox circles = new VBox();
Button btn = new Button("Touch me to make me flash ;)");
btn.setOnAction(e -> flash(circles));
for(int i = 0; i < 3; i++) {
circles.getChildren().add(new Circle(25, Color.RED));
}
circles.setSpacing(10);
circles.setAlignment(Pos.CENTER);
root.getChildren().addAll(circles, btn);
root.setSpacing(10);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 500, 500));
primaryStage.show();
}
private void flash(VBox root) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0.5), e -> colorProperty.set(Color.GOLD)),
new KeyFrame(Duration.seconds(1.0), e -> colorProperty.set(Color.RED))
);
timeline.setCycleCount(5);
timeline.play();
timeline.setOnFinished(e -> System.out.println("Do something here"));
for(Node circle : root.getChildren()) {
final Circle c = (Circle) circle;
c.fillProperty().bind(colorProperty);
}
}
public static void main(String[] args) {
launch(null);
}
}