JavaFX - 使多个对象上的Timeline对象执行一次EventHandler

JavaFX - making Timeline object on multiple objects execute EventHandler once

我现在正在使用 JavaFX,遇到了 运行 问题。我创建了一个简单示例来演示我面临的问题。

我的问题是,我在多个匿名 Circle 对象上设置了一个 Timeline 对象,我希望在时间轴完成其 play() 方法后发生一个动作。为此,我设置了一个 setOnFinished 事件处理程序以在动画播放完毕后执行某些操作,但是,它会多次执行此逻辑,因为它正在处理多个对象。

这里我有一个简单的程序,可以将 3 个匿名对象添加到 VBox 并且有一个按钮将调用 flash() 方法在圆圈上启动时间轴动画。

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"));
        }
    }
}

flash()方法中可以看到有一个EventHandler执行这行代码:

System.out.println("Do something here")

我希望它只执行一次,但它执行了 3 次,因为 Timeline 对象设置在 3 个圆圈上。

如何才能让 EventHandler 只执行一次?

考虑对所有 Circle 个对象使用一个 TimeLine 个对象:

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);
    }
}