1

I am creating a JavaFX desktop app on which I am simulating some work load. I want the app to have a progress indicator that updates dynamically (with time passing at the moment) to show how the load process is progressing. This is my application class:

public class App extends Application {

@Override
public void init() throws InterruptedException{
    //Simulation of time consuming code.
    for(int i = 0; i<=10; i++) {
        notifyPreloader(new Preloader.ProgressNotification(i/10));
        System.out.println("Progress is being set by the app to: " + (i/10));
        Thread.sleep(500);
    }
}

@Override
public void start(Stage primaryStage) {
    Parent root;
    try {
        root = FXMLLoader.load(getClass().getResource("/gui/fxml/App.fxml"));
        Scene scene = new Scene(root, 600, 400);

        scene.getStylesheets().add("/gui/style/app.css");

        primaryStage.setScene(scene);
        primaryStage.setTitle("Hello World!");
        primaryStage.show();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

This is my preloader class:

public class AppPreloader extends Preloader {
private Stage preloaderStage;
private Parent root;
private Scene scene;
private ProgressIndicator progress_indicator;

@Override
public void start(Stage primaryStage) throws Exception {
    this.preloaderStage = primaryStage;
    this.preloaderStage.setScene(this.scene);
    this.preloaderStage.show();
    this.progress_indicator = (ProgressIndicator) scene.lookup("#progressIndicator");
}


@Override
public void init() throws Exception {
    root = FXMLLoader.load(getClass().getResource("/gui/fxml/AppPreloader.fxml"));
    Platform.runLater(new Runnable() { 
        @Override
        public void run() {
            scene = new Scene(root, 600, 400);
            scene.getStylesheets().add("/gui/style/appPreloader.css");
        }
    });
}

@Override
public void handleProgressNotification(ProgressNotification pn) {
    if(pn instanceof ProgressNotification){
        progress_indicator.setProgress(pn.getProgress());
        System.out.println("Progress is being set by the handle method to: " + pn.getProgress());
    }
}

@Override
public void handleStateChangeNotification(StateChangeNotification evt) {
    if (evt.getType() == StateChangeNotification.Type.BEFORE_START) {
        preloaderStage.hide();
    }
}    
}

Whit the print sentences I've been able to identify two problems: First, the handleProgressNotification method is being called twice, once to be set to 0 and other to be set to 1, before the loop of the init method of the App class starts. Who is making the call? How can I avoid it?

The second problem is that the print sentence inside the init method of the app class is always printing 0.0. How can that be possible? Is it a matter of concurrency?

In addition I need to say that I've checked both of this questions (progressbar in preloader does not update and javafx preloader not updating progress) and didn't find a solution for my problem.

Thanks a lot for your time.

Drubio
  • 898
  • 9
  • 23

1 Answers1

1

First, you're not seeing the progress values you expect because you are using integer arithmetic: i and 10 are both integers, so i/10 is 0 for 0 <= i < 10 and 1 when i=10.

Second, the handleProgressNotification and handleStateChangeNotification methods are part of the lifecycle of the application that are related to loading the resources. These are really leftovers from the days when JavaFX still supported web deployments and are probably of limited use now.

To receive notifications from the application, you need to override the handleApplicationNotification(...) method instead. Here is a corrected version of the two classes (also modified to be stand-alone so they can be copied and run: please provide these kinds of examples in your questions) that works:

package application;

import javafx.application.Application;
import javafx.application.Preloader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class App extends Application {

    @Override
    public void init() throws InterruptedException{
        //Simulation of time consuming code.
        for(int i = 0; i<=10; i++) {
            notifyPreloader(new Preloader.ProgressNotification(i/10.0));
            System.out.println("Progress is being set by the app to: " + (i/10.0));
            Thread.sleep(500);
        }
        notifyPreloader(new Preloader.StateChangeNotification(Preloader.StateChangeNotification.Type.BEFORE_START));
    }

    @Override
    public void start(Stage primaryStage) {
        Parent root;
            root = new StackPane(new Label("Hello World"));
            Scene scene = new Scene(root, 600, 400);

            primaryStage.setScene(scene);
            primaryStage.setTitle("Hello World!");
            primaryStage.show();

    }

}
package application;

import javafx.application.Preloader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class AppPreloader extends Preloader {
    private Stage preloaderStage;
    private Parent root;
    private Scene scene;
    private ProgressIndicator progress_indicator;

    @Override
    public void start(Stage primaryStage) throws Exception {
        progress_indicator = new ProgressIndicator();
        root = new StackPane(progress_indicator);
        scene = new Scene(root, 600, 400);
        this.preloaderStage = primaryStage;
        this.preloaderStage.setScene(this.scene);
        this.preloaderStage.show();
    }


    @Override
    public void handleApplicationNotification(PreloaderNotification pn) {
        if (pn instanceof ProgressNotification) {
           //expect application to send us progress notifications 
           //with progress ranging from 0 to 1.0
           double v = ((ProgressNotification) pn).getProgress();
           progress_indicator.setProgress(v);            
        } else if (pn instanceof StateChangeNotification) {
            StateChangeNotification scn = (StateChangeNotification) pn ;
            if (scn.getType() == StateChangeNotification.Type.BEFORE_START) {
                preloaderStage.hide();
            }
        }
    } 
}
James_D
  • 177,111
  • 13
  • 247
  • 290
  • Thanks, the second part of the answer was critic and I could not find information about it before your answer. – Drubio Feb 16 '18 at 09:51
  • @Drubio I just used [this](https://docs.oracle.com/javafx/2/deployment/preloaders.htm) (which is old; again, I think there's not much use case for preloaders now), and the [Javadocs](https://docs.oracle.com/javase/9/docs/api/javafx/application/Preloader.html#handleProgressNotification-javafx.application.Preloader.ProgressNotification-). – James_D Feb 16 '18 at 12:13