问题
I am trying to make a loader dialog where the user can know that the program is loading what was requested and that the program is running as expected.
But as so, I need to join() the parser thread and before keep going on the main and this makes the dialog blank.
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();
}
}
}
If, I comment/ remove this code:
try {
parser.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
Then, I can see the thread loading as expected, otherwise, I can only see a blank screen that will be in the final state (the last message and the full progress bar).
while(!loadingLabel.getText().contains("one")){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
And even while(parser.isAlive()); without success.
My question is: How can I wait for my thread and keep the UI working?
回答1:
You should never block the UI thread.
Typical pattern for background processing looks like follows:
- Change UI to indicate that background processing is in progress (show progress bar, disable buttons, etc)
- Start a background thread
- In the background thread, after processing has finished, change the UI back and display processing results. Code that does it should be invoked using Platform.runLater() (as well as any other code that interacts with the UI from background threads)
Simplified example:
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();
}
});
The code above is not very elegant, you may want to use thread pools and/or ListenableFutures to make it look better.
来源:https://stackoverflow.com/questions/24578517/javafx-multithreading-joining-threads-wont-update-the-ui