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?
You should never block the UI thread.
Typical pattern for background processing looks like follows:
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 ListenableFuture
s to make it look better.