JavaFX - 在两者之间暂停调用UI更新方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaFX - 在两者之间暂停调用UI更新方法相关的知识,希望对你有一定的参考价值。
我正在研究一个JavaFx学校项目,我试图将imageView
移动到GridPane
。应该在UI上顺序地可视化/更新运动。
我尝试了AnimationTimers
和Threads
以及其他所有东西,但我不是在这个领域经验丰富。我在我的Thread
开始一个PlaygroundController
,并在方法结束时每次都打电话给睡觉。
在第二个代码片段中,我创建了一个PlaygroundController
类的对象,并连续三次调用这些方法。我的目标是使用ImageView
方法将moveRight()
1列向右移动,暂停一秒然后向下移动调用moveDown()
方法,依此类推。
使用我的代码,它会在3秒延迟后一起完成所有3种方法。
private Thread thread;
public PlaygroundController() {
thread = new Thread();
thread.start();
}
public void run(ActionEvent evt) throws InterruptedException {
Level1Solution level = new Level1Solution(this);
}
public void moveRight() throws InterruptedException {
if(GridPane.getColumnIndex(avatarIcon) < 10) {
GridPane.setColumnIndex(avatarIcon, GridPane.getColumnIndex(avatarIcon) +1);
}else{
Alert alert = new Alert(AlertType.WARNING);
alert.setTitle("Fehler!");
alert.setHeaderText("Weiter geht es nicht!");
alert.setOnHidden(evt -> Platform.exit());
alert.show();
}
avatarColumn = GridPane.getColumnIndex(avatarIcon);
Thread.sleep(1000);
}
第二个片段:
package controller;
public class Level1Solution {
PlaygroundController solution;
public Level1Solution(PlaygroundController solution) throws InterruptedException {
this.solution = solution;
solution.moveRight();
solution.moveDown();
solution.moveRight();
}
}
你的Level1Solution
构造函数可能是在FX Application Thread上执行的。它在PlaygroundController
实例上调用了三个方法,每个方法(可能)改变UI元素的位置,然后暂停当前(即FX应用程序)线程一秒钟。
注意你在PlaygroundController
中创建的线程并没有真正做任何事情:它没有Runnable
并且没有覆盖它自己的run()
方法,所以它基本上什么都不做。你启动那个线程 - 所以它在后台执行它的run()
方法,它会立即退出(因为它没有任何关系)。
FX应用程序线程负责呈现UI。因为您在FX应用程序线程上执行Level1Solution
构造函数,所以在该构造函数完成之前无法呈现该UI;即三秒后,UI将反映您对其所做的所有更改。
出于这样的原因,您永远不应该阻止FX应用程序线程。另请注意,必须在该线程上对UI进行实际更改。
要在特定时间点在FX应用程序线程上执行代码,您应该使用某种animation。对于您的用例,Timeline
可以正常工作;只需为每个时间点定义关键帧,使用调用方法的事件处理程序:
public class Level1Solution {
PlaygroundController solution;
public Level1Solution(PlaygroundController solution) throws InterruptedException {
this.solution = solution;
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(0), e -> solution.moveRight()),
new KeyFrame(Duration.seconds(1), e -> solution.moveDown()),
new KeyFrame(Duration.seconds(2), e -> solution.moveRight()));
timeline.play();
}
}
当然,从Thread
删除PlaygroundController
和暂停:
public PlaygroundController() {
}
public void run(ActionEvent evt) throws InterruptedException {
Level1Solution level = new Level1Solution(this);
}
public void moveRight() throws InterruptedException {
if(GridPane.getColumnIndex(avatarIcon) < 10) {
GridPane.setColumnIndex(avatarIcon, GridPane.getColumnIndex(avatarIcon) +1);
}else{
Alert alert = new Alert(AlertType.WARNING);
alert.setTitle("Fehler!");
alert.setHeaderText("Weiter geht es nicht!");
alert.setOnHidden(evt -> Platform.exit());
alert.show();
}
avatarColumn = GridPane.getColumnIndex(avatarIcon);
// never block the UI thread:
// Thread.sleep(1000);
}
如果你有一个特定的要求,Level1Controller
构造函数必须调用这三种方法,而不是以任何方式处理暂停本身,那么它会变得更加困难。一种方法是将您调用的方法提交给单个线程执行程序,其中提交的任务执行UI操作,然后暂停一段时间。单个线程上的暂停将确保在执行任何其他任务之前有一段时间。请注意,在这种情况下,任务将在后台线程上,因此UI的实际更改必须包含在对Platform.runLater(...)
的调用中,以便在FX Application Thread上执行它们。
这看起来像是:
public class PlaygroundController {
private ExecutorService exec ;
public PlaygroundController() {
// single thread executor using a daemon thread
// (so it will not prevent application exit)
exec = Executors.newSingleThreadExecutor(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
}
public void run(ActionEvent evt) throws InterruptedException {
Level1Solution level = new Level1Solution(this);
}
public void moveRight() throws InterruptedException {
Runnable task = () -> {
Platform.runLater(() -> {
if(GridPane.getColumnIndex(avatarIcon) < 10) {
GridPane.setColumnIndex(avatarIcon, GridPane.getColumnIndex(avatarIcon) +1);
} else {
Alert alert = new Alert(AlertType.WARNING);
alert.setTitle("Fehler!");
alert.setHeaderText("Weiter geht es nicht!");
alert.setOnHidden(evt -> Platform.exit());
alert.show();
}
avatarColumn = GridPane.getColumnIndex(avatarIcon);
});
Thread.sleep(1000);
};
exec.submit(task);
}
// ...
}
然后就是
public class Level1Solution {
PlaygroundController solution;
public Level1Solution(PlaygroundController solution) throws InterruptedException {
this.solution = solution;
solution.moveRight();
solution.moveDown();
solution.moveRight();
}
}
应该达到预期的效果。这种方法似乎有点过于复杂:上面的动画解决方案更为可取。
以上是关于JavaFX - 在两者之间暂停调用UI更新方法的主要内容,如果未能解决你的问题,请参考以下文章
JavaFX 媒体 - 暂停();方法使 MediaPlayer 快进?