404KnowledgeNotFound
404KnowledgeNotFound

Reputation: 181

Java FX showing a LoadingScreen while code and other objects are running/drawn

So I want to display a Loading Screen(simple Stackpane with an ProgressIndicator on top of a blur etc..) over my Desktop Application while another Screen is loading data from the database and drawing itself. Up until now I used a Task in a new Thread to "split" my loading animation running in the FX thread from the logic in the background, problem is this time logic and drawing objects on the screen in the bakckground are heavily intervowen and I cannot split them without stupenduous amount of work.

So I found out about AnimationTimer, which apparently is called on every frame and sends a pulse which causes an equivalent to a repaint() in Swing. And so I tried to use it thus:

public void showApplicationScreen(){
    public void addAndShowTransporterStatus() {
        AnimationTimer at = new AnimationTimer() {
            int i;
            @Override
            public void handle(long now) {
                i++;
                LOG.info(i);  //testing to see frames per second
            }
        };
    at.start();
    showLoadingIndicator(true);
    loadDataFromDBandDrawObjects();
    showLoadingIndicator(false);
    at.stop();
}

Is there some kind of trick to it that I am missing? Or some other (simple) way?

I cant believe something so simple is so complicated to do. Gotta say I wish every Node had a repaintAtIntervall(double timespan) method that would suspend everything else the application is doing atm and repaint itself before continuing with the normal flow. Might not be pretty but it sure as hell would be useful.

Upvotes: 0

Views: 2752

Answers (1)

James_D
James_D

Reputation: 209553

You really shouldn't need an AnimationTimer for something like this. If you are doing the initial loading in a background thread, use a Task. Show a loading screen and hide it in the task's onSucceeded handler. You can create node instances in a background thread as long as they are not part of the scene graph, so while it's not a particularly good design, you can do something like:

Task<Parent> createMainScene = new Task<Parent>() {
    @Override
    public Parent call() {
        Parent root = ... ;
        // load data etc., create structure below root
        // call updateMessage(...) to update a status message if needed
        // call updateProgress(...) to update the progress if needed
        // ...
        return root ;
    }
};

ProgressBar pBar = new ProgressBar();
pBar.progressProperty().bind(createMainScene.progressProperty());
Label statusLabel = new Label();
statusLabel.textProperty().bind(createMainScene.messageProperty());
VBox root = new VBox(5, statusLabel, pBar);
Stage loadingStage = new Stage(new Scene(root));
loadingStage.show();

createMainScene.setOnSucceeded(e -> {
    primaryStage.setScene(new Scene(createMainScene.getValue()));
    primaryStage.show();
    loadingStage.hide();
});

new Thread(createMainScene).start();

A better (more properly-separated) design would be to have the task just load the application data and process it, and return an object encapsulating the data. Then in the onSucceeded handler you would create the UI from the data (which should not take a long time). However, it sounds like you cannot do that with the current code with which you're working.

Upvotes: 2

Related Questions