【问题标题】:Return a value after JavaFX Timeline has finished在 JavaFX 时间轴完成后返回一个值
【发布时间】:2018-08-27 10:36:19
【问题描述】:

我在 JavaFX 中使用时间轴对Label 进行倒计时:

timeline.setCycleCount(6);
timeline.play();

我想在时间线结束后返回一个值:

return true;

但是,似乎该值会立即返回,并且时间线是并行运行的。如何等到时间线完成倒计时,然后在不阻塞时间线的情况下返回值?

编辑:

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

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

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

}
return true;

(此解决方案不会更新倒计时。)

编辑 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”。

【问题讨论】:

  • 请提供一个minimal reproducible example 来说明问题。
  • 我添加了一个 MCVE 以使其更清晰。
  • good :) 只是(与您的问题无关):请学习 java 命名约定并遵守它们
  • 您只是错误地使用了时间线,正如答案中已经解释的那样。您为什么不采纳这些建议并向我们展示为什么它们不符合您的要求?

标签: java javafx timeline


【解决方案1】:

由于Timeline 继承自Animation,您可以使用setOnFinished 定义要在时间线末尾发生的操作。

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

【讨论】:

  • 已经试过了。如何将其中的值返回给外部函数?
  • 如果不延迟时间轴所在线程的执行,您将无法做到这一点。你用这个返回值做什么?
  • @user3776738 请参阅我的更新答案,了解如何在您的情况下使用setOnFinished
【解决方案2】:

如果您真的想等到时间线结束,您可以使用CountDownLatchSemaphore 以及setOnFinished。像下面这样的东西应该可以工作:

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

【讨论】:

    【解决方案3】:

    您正试图在一个线程中等待另一个线程中的工作结果。这就是 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 应用程序线程”上运行此方法,因为它会阻止 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");
    });
    

    【讨论】:

    • 恕我直言,使用Consumer&lt;Boolean&gt; 会更接近原始代码(您将值传递给“完成的处理程序”),但在这种情况下,Runnable 应该可以解决问题......( +1)
    【解决方案4】:

    在您拥有的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 命名约定:标识符(名称)以小写字母开头。
    • 正确缩进您的代码,以便于阅读。

    【讨论】:

      猜你喜欢
      • 2018-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-21
      • 1970-01-01
      • 1970-01-01
      • 2018-08-13
      • 2019-06-15
      相关资源
      最近更新 更多