简体   繁体   中英

Why does my JavaFX Preloader occasionally show up gray/black and other times it correctly loads?

I'm trying to get my JavaFX Preloader splash sccreen to show up before my application. I'm using Eclipse IDE and when I click "run", half the time the splash screen will display correctly and the other half of the time I will get a gray or black screen instead of where the image should be.

I'm not sure what the issue is to cause it to only display correctly sometimes.

SplashController :

public class SplashController extends Preloader {
  private static final double WIDTH = 676;
  private static final double HEIGHT = 227;
  private Stage preloaderStage;
  private Label progressText;
  private Pane splashScreen;

 public SplashController() {}    

 @Override
  public void init() throws Exception {
    ImageView splash =
        new ImageView(new Image(Demo.class.getResource("pic.png").toString()));
    progressText =
        new Label("VERSION: " + getVersion() + " ~~~ Loading plugins, please wait...");
    splashScreen = new VBox();
    splashScreen.getChildren().addAll(splash, progressText);
    progressText.setAlignment(Pos.CENTER);
  }

  @Override
  public void start(Stage primaryStage) throws Exception {
    this.preloaderStage = primaryStage;
    Scene splashScene = new Scene(splashScreen);
    this.preloaderStage.initStyle(StageStyle.UNDECORATED);
    final Rectangle2D bounds = Screen.getPrimary().getBounds();
    this.preloaderStage.setScene(splashScene);
    this.preloaderStage.setX(bounds.getMinX() + bounds.getWidth() / 2 - WIDTH / 2);
    this.preloaderStage.setY(bounds.getMinY() + bounds.getHeight() / 2 - HEIGHT / 2);
    this.preloaderStage.show();
  }
}

And then in my main class Demo I simply have:

public class Demo extends Application {
  @Override
  public void start(Stage stage) throws Exception {
     FXMLLoader loader = new 
     FXMLLoader(Demo.class.getResource("FXMLDocument.fxml"));
     GridPane root = loader.load();

                  --------other app  code here---------
  }

  public static void main(String[] args) {
    LauncherImpl.launchApplication(Demo.class, SplashController.class, args);
  }

}

Likely, you are executing some long running process on the JavaFX application thread or a thread involved in the application startup, which prevents the smooth operation of the preloader.

I suggest you review an Oracle Preloader sample and compare to your application. Ensure that you are using concurrent features like Task correctly, similar to the linked example. Check the linked sample works in your environment.

Source code (just copied from the Oracle Preloader sample link)

Note how in the start method of the main LongAppInit application class, that a Task and thread is spawned to ensure that the long application initiation does not take place on the JavaFX application thread. Also see how the notifyPreloader() method of application is called at various times within the long application initialization to let the preloader know of the current state of the initialization process so that it can reflect the progress accurately in the UI in real time.

LongAppInitPreloader.java

public class LongAppInitPreloader extends Preloader {
    ProgressBar bar;
    Stage stage;
    boolean noLoadingProgress = true;

    private Scene createPreloaderScene() {
        bar = new ProgressBar(0);
        BorderPane p = new BorderPane();
        p.setCenter(bar);
        return new Scene(p, 300, 150);
    }

    public void start(Stage stage) throws Exception {
        this.stage = stage;
        stage.setScene(createPreloaderScene());
        stage.show();
    }

    @Override
    public void handleProgressNotification(ProgressNotification pn) {
        //application loading progress is rescaled to be first 50%
        //Even if there is nothing to load 0% and 100% events can be
        // delivered
        if (pn.getProgress() != 1.0 || !noLoadingProgress) {
          bar.setProgress(pn.getProgress()/2);
          if (pn.getProgress() > 0) {
              noLoadingProgress = false;
          }
        }
    }

    @Override
    public void handleStateChangeNotification(StateChangeNotification evt) {
        //ignore, hide after application signals it is ready
    }

    @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();
           if (!noLoadingProgress) {
               //if we were receiving loading progress notifications 
               //then progress is already at 50%. 
               //Rescale application progress to start from 50%               
               v = 0.5 + v/2;
           }
           bar.setProgress(v);            
        } else if (pn instanceof StateChangeNotification) {
            //hide after get any state update from application
            stage.hide();
        }
    }  
 }

LongAppInit.java

public class LongInitApp extends Application {
    Stage stage;
    BooleanProperty ready = new SimpleBooleanProperty(false);

    private void longStart() {
        //simulate long init in background
        Task task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                int max = 10;
                for (int i = 1; i <= max; i++) {
                    Thread.sleep(200);
                    // Send progress to preloader
                    notifyPreloader(new ProgressNotification(((double) i)/max));
                }
                // After init is ready, the app is ready to be shown
                // Do this before hiding the preloader stage to prevent the 
                // app from exiting prematurely
                ready.setValue(Boolean.TRUE);

                notifyPreloader(new StateChangeNotification(
                    StateChangeNotification.Type.BEFORE_START));

                return null;
            }
        };
        new Thread(task).start();
    }

    @Override
    public void start(final Stage stage) throws Exception {
        // Initiate simulated long startup sequence
        longStart();

        stage.setScene(new Scene(new Label("Application started"), 
            400, 400));

        // After the app is ready, show the stage
        ready.addListener(new ChangeListener<Boolean>(){
            public void changed(
                ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                    if (Boolean.TRUE.equals(t1)) {
                        Platform.runLater(new Runnable() {
                            public void run() {
                                stage.show();
                            }
                        });
                    }
                }
        });;                
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM