My initial fxml(say home.fxml
) has a lot of functionalities, hence it takes a lot of time to load completely. So to avoid the time gap between program start and fxml loading I introduced one more fxml(say loader.fxml
) with a gif image which should appear while the main fxml is loading.
The problem is the gif image in my loader.fxml is not moving as in the program is hanging until the home.fxml is loaded completely.
To avoid this I moved the home.fxml loading into a thread as shown in below code.
public class UATReportGeneration extends Application {
private static Stage mainStage;
@Override
public void start(Stage stage) {
Parent loaderRoot = null;
try {
loaderRoot = FXMLLoader.load(getClass().getResource("/uatreportgeneration/fxml/Loader.fxml"));
} catch (IOException ex) {
Logger.getLogger(UATReportGeneration.class.getName()).log(Level.SEVERE, null, ex);
}
Scene loadScene = new Scene(loaderRoot);
stage.setScene(loadScene);
stage.initStyle(StageStyle.UNDECORATED);
stage.getIcons().add(new Image(this.getClass().getResourceAsStream("/uatreportgeneration/Images/logo.png")));
stage.show();
mainStage = new Stage(StageStyle.UNDECORATED);
mainStage.setTitle("Upgrade Analysis");
mainStage.getIcons().add(new Image(this.getClass().getResourceAsStream("/uatreportgeneration/Images/logo.png")));
setStage(mainStage);
new Thread(() -> {
Platform.runLater(() -> {
try {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("/uatreportgeneration/fxml/Home.fxml"));
Scene scene = new Scene(root);
mainStage.setScene(scene);
mainStage.show();
stage.hide();
System.out.println("Stage showing");
// Get current screen of the stage
ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight()));
// Change stage properties
Rectangle2D bounds = screens.get(0).getVisualBounds();
mainStage.setX(bounds.getMinX());
mainStage.setY(bounds.getMinY());
mainStage.setWidth(bounds.getWidth());
mainStage.setHeight(bounds.getHeight());
System.out.println("thread complete");
} catch (IOException ex) {
Logger.getLogger(UATReportGeneration.class.getName()).log(Level.SEVERE, null, ex);
}
});
}).start();
}
public static Stage getStage() {
return mainStage;
}
public static void setStage(Stage stage) {
mainStage = stage;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
But after this code also my program is hanging(The gif image is not moving). If I load the fxml outside the Platform.runLater()
, I get the exception Not on FX Thread
.
I also tired using Task()
but from that the gif image is moving but the fxml is not loading in the background, if I try to load the fxml outside Platform.runLater()
.
Can anyone please help me and tell me how can I correct the code so that my fxml loads in the background without disturbing the foreground process.
your approach using
Task
was already correct. you were just missing a bit more: you were just missing anotherPlatform#invokeLater()
to update the UI:Use a
Task
. You need to arrange to create the scene and update the stage on the FX Application Thread. The cleanest way is to use aTask<Parent>
:Here is a SSCCE using this technique:
main.fxml:
MainController (uses the
Task
approach shown above):test.fxml:
TestController:
Main application:
Notice that after pressing the button, you can still type in the text field during the five seconds it takes to "load" the FXML, so the UI is remaining responsive.
In addition to @James_D answer. As mentioned by him, Stages and Scene should not be added in background thread, they should be added only in FX main thread.
I have added
tooltips
in myhome.fxml
, which is nothing but aPopupWindow
. Therefore to the background thread, it appeared as a new stage. Hence it threwIllegalStateException
. After removing thetooltips
from the fxml, the fxml was able to load as a background process as there were no stages created in that thread.