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"));
如果你真的想等到时间线完成,你可以使用 CountDownLatch
或 Semaphore
和 setOnFinished
。像下面这样的东西应该可以工作:
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");
});
除了 KeyFrame
的 setOnFinished
之外,还使用 Animation
的 setOnFinished
(Timeline
是 Animation
).我修改了您的 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);
}
}
在每个循环中,将执行 KeyFrame
的 setOnFinished
,将计数减 1。当整个动画完成(所有循环)时,Timeline
的 setOnFinished
将被执行。
备注:
- 使用
ctime
变量计算循环数,使它们匹配。
- 使用 Java 命名约定:标识符(名称)以小写字母开头。
- 正确缩进您的代码,使其更易于阅读。
我正在使用 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"));
如果你真的想等到时间线完成,你可以使用 CountDownLatch
或 Semaphore
和 setOnFinished
。像下面这样的东西应该可以工作:
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");
});
除了 KeyFrame
的 setOnFinished
之外,还使用 Animation
的 setOnFinished
(Timeline
是 Animation
).我修改了您的 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);
}
}
在每个循环中,将执行 KeyFrame
的 setOnFinished
,将计数减 1。当整个动画完成(所有循环)时,Timeline
的 setOnFinished
将被执行。
备注:
- 使用
ctime
变量计算循环数,使它们匹配。 - 使用 Java 命名约定:标识符(名称)以小写字母开头。
- 正确缩进您的代码,使其更易于阅读。