Return JavaFX 时间轴完成后的值

Return a value after JavaFX Timeline has finished

我正在使用 JavaFX 中的时间轴对 Label:

进行倒计时
timeline.setCycleCount(6);
timeline.play();

我想在时间线完成后 return 一个值:

return true;

但是,该值似乎立即 returned 并且时间线并行运行。我怎样才能等到时间线完成倒计时然后 return 值而不阻塞时间线?

编辑:

为了更清楚,我已经试过了:

new Thread(() -> {
    timeline.play();
}).start();

while(!finished){ // finished is set to true, when the countdown is <=0

}
return true;

(This solution doesn't update the countdown.)

编辑 2:

这是一个最小的、完整的和可验证的例子:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;


public class CountdownTest extends Application {

    private Label CountdownLabel;
    private int Ctime;

    @Override
    public void start(Stage primaryStage) {

        CountdownLabel=new Label(Ctime+"");

        StackPane root = new StackPane();
        root.getChildren().add(CountdownLabel);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Countdown Test");
        primaryStage.setScene(scene);
        primaryStage.show();

        Ctime=5;

        if(myCountdown()){
            CountdownLabel.setText("COUNTDOWN FINISHED");
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    public boolean myCountdown(){
         final Timeline timeline = new Timeline(
                            new KeyFrame(
                                    Duration.millis(1000),
                                    event -> {
                                    CountdownLabel.setText(Ctime+"");
                                    Ctime--;

                                    }

                            )
                    );
         timeline.setCycleCount(6);
         timeline.play();
    return true;
    }

}

你可以看到它首先显示"COUNTDOWN FINISHED"并倒数到0,而不是从倒计时开始倒数到"COUNTDOWN FINISHED"。

作为 Timeline inherits from Animation, you can use setOnFinished 定义在时间线结束时发生的动作。

timeline.setCycleCount(6);
timeline.play();
timeline.setOnFinished(event -> countdownLabel.setText("COUNTDOWN FINISHED"));

如果你真的想等到时间线完成,你可以使用 CountDownLatchSemaphoresetOnFinished。像下面这样的东西应该可以工作:

CountDownLatch latch = new CountDownLatch(1);
timeline.setCycleCount(6);
timeline.setOnFinished(event -> latch.countDown());
timeline.play();
latch.await();
return true;

您正试图在一个线程中等待另一个线程中的工作结果。这就是 synchronisation 的创建目的!例如。 java.util.concurrent.Semaphore:

public boolean waitForTimeline()  {
    Semaphore semaphore = new Semaphore(0);

    System.out.println("starting timeline");
    Timeline t = new Timeline();
    t.getKeyFrames().add(new KeyFrame(Duration.seconds(2)));
    t.setOnFinished((e)-> {
        System.out.println("releasing semaphore"); 
        semaphore.release();
    });
    t.play();

    System.out.println("waiting for timeline to end");
    try {
        semaphore.acquire();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
        return false;
    }
    return true;
}

但请注意,您不能在 "JavaFX Application Thread" 上 运行 此方法,因为它会阻止 UI 更新。 运行 它在一个单独的线程上:

new Thread(()-> { 
    System.out.println("returned from timeline with " + waitForTimeline()); 
}).start();

或者,更好的是,使用您在 return 之后执行的逻辑创建一个监听器,然后从 t.setOnFinished() 调用该监听器。对于您的示例,它将是:

public void myCountdown(Runnable onSuccess){
    //...
    timeline.setOnFinished((e)-> {
        onSuccess.run();
    });
}

和相应的调用:

myCountdown(()->{
    CountdownLabel.setText("COUNTDOWN FINISHED");
});

除了 KeyFramesetOnFinished 之外,还使用 ​​AnimationsetOnFinishedTimelineAnimation).我修改了您的 MCVE 以展示如何针对您的情况执行此操作:

public class CountdownTest extends Application {

    private Label countdownLabel;
    private int ctime = 5;

    @Override
    public void start(Stage primaryStage) {
        countdownLabel = new Label();

        StackPane root = new StackPane();
        root.getChildren().add(countdownLabel);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Countdown Test");
        primaryStage.setScene(scene);
        primaryStage.show();

        myCountdown();
    }

    public void myCountdown() {
        final Timeline timeline = new Timeline(new KeyFrame(Duration.millis(1000),
            event -> {
                countdownLabel.setText(ctime + "");
                ctime--;
            }
        ));
        timeline.setCycleCount(ctime + 1);
        timeline.setOnFinished(e -> countdownLabel.setText("COUNTDOWN FINISHED"));
        timeline.play();
    }

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

在每个循环中,将执行 KeyFramesetOnFinished,将计数减 1。当整个动画完成(所有循环)时,TimelinesetOnFinished 将被执行。

备注:

  • 使用 ctime 变量计算循环数,使它们匹配。
  • 使用 Java 命名约定:标识符(名称)以小写字母开头。
  • 正确缩进您的代码,使其更易于阅读。