我正在尝试在imageView
上移动GridPane
的 JavaFx 学校项目。应该在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();
}
}
答案 0 :(得分:1)
您的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应用程序线程上执行它们。
这看起来像是:
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();
}
}
应该达到预期的效果。这种方法似乎有点过于复杂:上面的动画解决方案更为可取。