Reputation: 71
I need someone explain me how Preloader works in JavaFX application. I have already found out the lifecycle and I know that first is called start(..) of preloader and then start(..) of Application but is this two methods are called from the same Thread? I have tried to make a simple app in start method of application a put Thread.sleep(8000) and in the start of Preloader I created a simple stage and show it. But when I run the application it just freeze with black stage and only after 8 seconds shows preloader stage correctly, but why?
Update my post with source code:
public class PreloaderApp extends Preloader {
public void init(){
System.out.println("Thread Preloader ID:"+ Thread.currentThread().getId());
System.out.println(" ------ ");
Pane effectRegion = LoaderFrame.getInstance().getEffectPane();
JFXFillTransition fill = new JFXFillTransition();
fill.setDuration(Duration.millis(400));
fill.setRegion(effectRegion);
fill.setAutoReverse(true);
fill.setCycleCount(Animation.INDEFINITE);
fill.setFromValue(Color.WHITE);
fill.setToValue(Color.rgb(0,77,147));
fill.play();
}
@Override
public void start(Stage primaryStage){
System.out.println("Thread Preloader(start) ID:"+ Thread.currentThread().getId());
System.out.println(" ------ ");
LoaderFrame.getInstance().show();
}
}
public class LoaderFrame extends Stage {
private static final LoaderFrame instance = new LoaderFrame();
public static LoaderFrame getInstance(){
return instance;
}
private Scene scene;
private AnchorPane root;
private BorderPane wraper;
private StackPane effectPane;
private JFXButton loaderPathButton;
public LoaderFrame(){
initScene();
this.initStyle(StageStyle.TRANSPARENT);
this.getIcons().add(new Image("file:imgs/favico/icon48.png"));
}
public void initScene(){
FXMLLoader loader = new FXMLLoader(Main.class.getResource("xml/loader_frame.fxml"));
root = null;
try {
root = loader.load();
wraper = (BorderPane) root.lookup("#rootPane");
loaderPathButton = (JFXButton) root.lookup("#loaderPathButton");
effectPane = (StackPane) root.lookup("#effectPane");
scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
scene.getStylesheets().add("file:css/loader.css");
this.setScene(scene);
} catch (IOException e) {
e.printStackTrace();
}
}
public Pane getEffectPane(){
return effectPane;
}
}
Upvotes: 1
Views: 1447
Reputation: 6952
To investigate the Preloader
lifecycle you could add the following line to your Preloader
class:
@Override
public void handleStateChangeNotification(StateChangeNotification info) {
System.out.println("state: " + info.getType());
}
In your Application
class you can add a System.out.prinln
message in the init
/ start
method. This will show the Preloader
running through its different states: BEFORE_LOAD
, BEFORE_INIT
, BEFORE_START
.
As the Preloaders start method
is called on the JavaFXThread
you have to wrap your call to Thread.sleep
in Platform.runlater
.
Note that preloaders are subject to the same rules as other JavaFX applications including FX threading rules. In particular, the class constructor and init() method will be called on a non-FX thread and start() will be executed on the FX application thread. This also means that the application constructor/init() will run concurrently with preloader start().
If you need to do some time consuming initialization, you should do it in your Apps init
method. When the Preloader state
changes to BEFORE_START
you can hide your Preloader stage
public class App extends Application {
private static final int PRELOADER_SHOWTIME_MILLIS = 2000;
@Override
public void init() throws Exception {
long start = System.currentTimeMillis();
// time consuming initializations
long duration = System.currentTimeMillis() - start;
long remainingShowTime = PRELOADER_SHOWTIME_MILLIS - duration;
if(remaingShowTime > 0 ){
Thread.sleep(remainingShowTime);
}
}
@Override
public void start(Stage primaryStage) {
StackPane root = new StackPane(new Label("application"));
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
public class TestPreloader extends Preloader {
private Stage stage;
@Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
Rectangle rect = new Rectangle(200, 200, Color.RED);
FadeTransition transition = new FadeTransition(Duration.seconds(2), rect);
transition.setFromValue(0);
transition.setToValue(1);
Label lbl = new Label("preloader");
StackPane root = new StackPane(rect, lbl);
primaryStage.setScene(new Scene(root));
primaryStage.show();
transition.play();
}
@Override
public void handleStateChangeNotification(StateChangeNotification info) {
if (info.getType() == Type.BEFORE_START) {
stage.hide();
}
}
}
public class Main {
public static void main(String[] args) {
LauncherImpl.launchApplication(App.class, TestPreloader.class, args);
}
}
Upvotes: 1