我有以下代码:
@FXML
private void test(){
textField.setText("Pending...");
boolean passed = doStuff();
if(passed){
textField.setText("OK");
} else {
textField.setText("Error");
}
}
我想要实现的是,在doStuff()在GUI的textField中完成其工作时,应将其写为“ Pending ...”,并在完成后立即将其更改为“ OK” /“错误”。
我希望在doStuff运行时阻止GUI,以便用户必须等待并且不能单击其他内容。
但是发生的事情是,一旦我开始测试,它就会执行doStuff(),但是只会使用“ OK” /“ Error”来更新textField,但我从未看到“ Pending ...”。
我觉得我已经以某种方式更新了GUI,但是我不确定应该如何完成。
更新: 我试图在另一个线程中移动doStuff:
@FXML
private void test(){
textField.setText("Pending...");
Thread t = new Thread(){
public void run(){
boolean passed = doStuff();
if(passed){
textField.setText("OK");
} else {
textField.setText("Error");
}
}
};
t.start();
t.join();
}
如果我要删除t.join(),它将起作用。命令,但不会阻止用户界面。所以我现在很茫然。
谢谢
答案 0 :(得分:1)
您绝不能在 JavaFX Application Thread 上运行长时间运行的任务。这样做将防止所述线程执行任何与GUI相关的操作,从而导致UI冻结。这使您的用户感到难过。但是,将长期运行的任务放在后台任务上的尝试存在缺陷。您调用Thread.join
,它将阻塞调用线程,直到目标线程死亡为止;实际上,这与在调用线程上运行任务是一样的。
要快速修复示例,您可以执行以下操作:
@FXML
private void test(){
textField.setText("Pending...");
Thread t = new Thread(){
@Override public void run(){
boolean passed = doStuff();
Platform.runLater(() -> {
if(passed){
textField.setText("OK");
} else {
textField.setText("Error");
}
});
}
};
t.start();
}
这将创建一个线程,将其启动,并使其在后台运行,同时让 JavaFX Application Thread 继续执行所需的操作。在后台线程内,您必须在TextField
调用内更新Platform.runLater(Runnable)
。这是必需的,因为您必须永远不要从JavaFX Application Thread之外的其他线程更新实时场景图;这样做将导致不确定的行为。另外,您应该查看“implements Runnable” vs “extends Thread” in Java。这样做比较好,或者至少是惯用的:
Thread t = new Thread(() -> { /* background code */ });
您还可以使用javafx.concurrent.Task
,这可以使与 JavaFX Application Thread 的通信更加容易。一种选择是:
@FXML
private void test(){
textField.setText("Pending...");
Task<Boolean> task = new Task<>() {
@Override protected Boolean call() throws Exception {
return doStuff();
}
};
task.setOnSucceeded(event -> textField.setText(task.getValue() ? "Ok" : "Error"));
new Thread(task).start();
}
您还可以将TextField
绑定到message
的{{1}}属性,并在Task
方法内调用updateMessage("Pending...")
。如果可能的话,您甚至可以提供更详细的消息。
也就是说,自己创建并启动call
并不理想,您应该研究thread pooling(使用类似Thread
的方法)。您可能还想研究javafx.concurrent.Service
来“重用” ExecutorService
。
有关JavaFX并发的更多信息,请参见Concurrency in JavaFX并阅读javafx.concurrent
中类的文档。有关Java多线程的基础知识,请参见 The Java™Tutorials 中的Lesson: Concurrency。