我正在尝试创建一个加载程序对话框,用户可以在其中知道程序正在加载所请求的内容并且程序正在按预期运行。
但因此,我需要 join()
解析器线程,然后继续主线程,这会使对话框空白。
ParserTask.java
public class ParserTask extends Task<Void>{
@Override
protected Void call() throws Exception {
for(int i=0;i<10;i++){
//parse stuff
Thread.sleep(1000);
updateProgress(i,9);
updateMessage("foo no:"+ i);
}
updateMessage("done");
return null;
}
}
Main.java
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 300, 150);
stage.setScene(scene);
stage.setTitle("Progress Controls");
Button btn = new Button("call the thread");
btn.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
threadDialog();
}
});
VBox vb = new VBox();
vb.getChildren().addAll(btn);
scene.setRoot(vb);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private void threadDialog() {
Stage stageDiag = new Stage();
ParserTask pTask = new ParserTask();
final Label loadingLabel = new Label("");
loadingLabel.textProperty().bind(pTask.messageProperty());
final ProgressBar pbar = new ProgressBar();
pbar.setProgress(0);
pbar.setPrefHeight(20);
pbar.setPrefWidth(450);
pbar.progressProperty().bind(pTask.progressProperty());
GridPane grid = new GridPane();
grid.setHgap(14.0);
grid.add(loadingLabel, 0, 0);
grid.add(pbar, 1, 1);
Scene sceneDiag = new Scene(grid);
stageDiag.setScene(sceneDiag);
stageDiag.setTitle("Foo thread is loading");
stageDiag.show();
Thread parser = new Thread(pTask);
parser.start();
try {
parser.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如果,我评论/删除此代码:
try {
parser.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
然后,我可以看到线程按预期加载,否则,我只能看到处于最终状态的空白屏幕(最后一条消息和完整的进度条)。
while(!loadingLabel.getText().contains("one")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
甚至while(parser.isAlive());
也没有成功。
我的问题是:如何等待我的线程并保持 UI 正常工作?
最佳答案
您永远不应该阻塞 UI 线程。
后台处理的典型模式如下所示:
- 更改 UI 以指示后台处理正在进行(显示进度条、禁用按钮等)
- 启动后台线程
- 在后台线程中,处理完成后,将UI改回并显示处理结果。执行此操作的代码应使用
Platform.runLater()
调用(以及从后台线程与 UI 交互的任何其他代码)
简化示例:
btn.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
// 1
setBackgroundProcessing(true);
// 2
new Thread() {
public void run() {
doProcessing();
// 3
Platform.runLater(new Runnable() {
public void run() {
displayResults();
setBackgroundProcessing(false);
}
});
}
}.start();
}
});
上面的代码不是很优雅,您可能需要使用线程池和/或 ListenableFuture
来使其看起来更好。
关于JavaFX 多线程 - 连接线程不会更新 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24578517/