我有一个带网格的简单应用程序(GridPane)和一个带有开始/停止按钮的简单工具栏。我的按钮动作如下所示:
startButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
grid.change();
}
});
当我点击它时,网格就会发生变化。因为场景图形不是线程安全的,并且它只由FX应用程序线程呈现,所以我已经放置了一个服务,它应该更新我的网格:
private Service task = new Service(){
@Override
protected Task createTask() {
System.out.println("Task created");
return new Task<Void>() {
@Override
protected Void call() throws Exception {
for (int i = 0; i < 3; i++) {
System.out.println("calling");
Platform.runLater(new Runnable(){
@Override
public void run() {
System.out.println("running");
grid.changeGeneration();
try {
Thread.sleep(1500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
return null;
}
};
}
};
当我正在运行我的应用程序时,按下按钮后会给我这个
已创建任务 调用 调用 调用 赛跑 赛跑 运行
它重新计算网格模型,但它不想渲染视图,实际上在完成所有Platform.runLater之后,FX App Thread会渲染网格,但不是在每个running
之后。我的错误在哪里?如何在每个running
之后使FX App Thread渲染我的网格?
更新 得到了解决方案here,但仍然无法完全理解它拒绝工作的原因。
答案 0 :(得分:1)
这里的原因可能是您快速连续发布了三个Runnables
,并且FX队列没有机会在两者之间实际重新绘制。 由于缺少更好的名称,重绘不会立即执行,而是添加到事件队列的末尾。所以基本上,你的外部循环触发三个Runnables
,事件队列最终看起来像这样:| GridChange | GridChange | GridChange |重绘|重绘|重绘|。
此外,您的睡眠实际上阻止了事件队列 - 您永远不应该在Thread#sleep(...)
内调用Platform#runLater(...)
。您实际上阻止了3x1500毫秒的所有事件处理,包括重绘和用户输入。
将Thread.sleep()
移出Runnable
,您将解决这两个问题。事件队列将接收GridChange,有1.5秒更新UI,接收下一个GridChange等等......