0

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?

bmb
  • 361
  • 2
  • 13
  • https://stackoverflow.com/questions/52589640/change-javafx-circles-color-in-certain-time-using-multithreads/52594017?r=SearchResults&s=7|35.5427#52594017 – SedJ601 Feb 17 '20 at 04:43
  • Another [example of [using one animation tool to animate a number of objects](https://stackoverflow.com/a/59701768/3992939) – c0der Feb 17 '20 at 07:56

1 Answers1

3

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);
    }
} 
c0der
  • 18,467
  • 6
  • 33
  • 65