在后台加载JavaFX GUI

时间:2017-10-01 21:29:20

标签: java javafx

我正在尝试为我的程序创建一个启动画面,因为它需要一些时间才能启动。 问题是创建GUI需要一段时间(创建对话框,更新表等)。而且我无法将GUI创建移动到后台线程(如“Task”类),因为我将获得“Not on FXApplication Thread”异常。 我尝试使用:

Platform.runLater(new Runnable() {
            public void run() {
             //create GUI
  }
}

任务的“调用”方法:

public class InitWorker extends Task<Void> {

    private Model model;
    private ViewJFX view;

    public InitWorker(Model model) {
        this.model = model;
    }

    @Override
    protected Void call() throws Exception {
          View view = new View();
          Collection collection = new Collection();
          //do stuff
    }
}

当我在Swing中编写程序时,我可以在EventDispatchThread上显示和更新Splash Screen,而不需要任何真正的concurreny。代码看起来像这样:

public void build() {
        MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Menus");
        menuCreator = new MenuCreatorOld (model, this);
        menuCreator.createMenu();
        MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE, "Creating Toolbar");
        toolBar = menuCreator.createToolBar();

        createWesternPanelToolBar();

        shoppingPanel = new ShoppingListOld(model, this, collectionController, shoppingController, controller);

        centerTabbedPane = new JTabbedPane();
            MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Collection");
            collectionPanel = new CollectionOld(model, collectionController, this, controller);
            MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Wish List");
            wishPanel = new WishListOld(model, this, collectionController, wishController, controller);

            MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Folders Table");
             //and so on
}

public static void updateProgressBar(int progressValue, String text) {
    System.out.println("Loading Bar Value:"+progressValue);
    progressBar.setValue(progressValue);
    loadingLabel.setText(text);
    progressBar.setString(text);
}

在显示带加载栏的启动画面时,有没有办法在后台创建GUI?

编辑:

我查看了我的代码,并且能够将启动时间减少5秒。大多数对话框在创建数据时从数据库中提取数据。所以我将对话框的创建移动到了他们的getter方法中。这导致了3秒的改善。但我仍然想知道是否有在后台线程上创建GUI的方法。

编辑:

正如所建议的那样,我也尝试在“任务”中使用“RunLater”。 这样我就可以创建GUI并显示SplashScreen,但我无法更新进度条和进度标签,因为GUI创建会阻止JavaFX应用程序线程。只有在完全创建GUI后,才会更新进度条和标签。

这是一个你们可以运行的例子(我删除了启动画面,只保留了进度条和进度标签):

public class InitWorker extends Task<Void> {


    private static ProgressBar progressBar;
    private static Label progressLabel;
    private static double PROGRESS_MAX = 5; 
    private double loadingValue;

    public InitWorker() {
        loadingValue = 0;
    }

    @Override
    protected void succeeded() {
        System.out.println("Succeeded");
    }

    @Override
    protected void failed() {
        System.out.println("Failed");
    }

    @Override
    protected Void call() throws Exception {
        System.out.println("RUNNING");
        Platform.runLater(new Runnable() {
            public void run() {
                displaySplashScreen();
                for(int i=0; i<10; i++) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    updateProgressBar(loadingValue++, "Label "+i);
                    Stage stage = new Stage();
                    Label label = new Label("Label " + i);
                    VBox panel = new VBox();
                    panel.getChildren().add(label);
                    Scene scene = new Scene(panel);
                    stage.setScene(scene);
                    stage.centerOnScreen();
                    stage.show();
                }
//              updateProgressBar(1, "Initializing...");

            }});

        return null;
    }


    public void updateProgressBar(double loadingValue, String text) {
        progressBar.setProgress(loadingValue / PROGRESS_MAX);
        progressLabel.setText(text);
    }




    public static void displaySplashScreen() {

        Stage progressBarStage = new Stage();
        progressBar = new ProgressBar();
        Scene progressBarScene = new Scene(progressBar);
        progressBarStage.setScene(progressBarScene);

        Stage progressLabelStage = new Stage();
        progressLabel = new Label("Loading...");
        progressLabel.setPadding(new Insets(5));
        progressLabel.setStyle("-fx-background-color: red");
        Scene progressLabelScene = new Scene(progressLabel);
        progressLabelStage.setScene(progressLabelScene);

        double progressBarWidth = 500;
        double progressBarHeight = 75;

        //muss angezeigt werden, um sie abhängig von Größe zu positionieren
        progressBarStage.show();
        progressLabelStage.show();
        //

        progressBarStage.setWidth(progressBarWidth);
        progressBarStage.setHeight(progressBarHeight);
        progressBarStage.centerOnScreen();
        progressBarStage.centerOnScreen();
        progressLabelStage.setY(progressLabelStage.getY() + 25);


    }

}

1 个答案:

答案 0 :(得分:0)

请参阅Task documentation titled "A Task Which Modifies The Scene Graph",其中提供了一个示例:

final Group group = new Group();
Task<Void> task = new Task<Void>() {
    @Override protected Void call() throws Exception {
        for (int i=0; i<100; i++) {
            if (isCancelled()) break;
            final Rectangle r = new Rectangle(10, 10);
            r.setX(10 * i);
            Platform.runLater(new Runnable() {
                @Override public void run() {
                    group.getChildren().add(r);
                }
            });
        }
        return null;
    }
};

上面的示例通过100次runLater调用将矩形添加到场景图中。更有效的方法是将矩形添加到未附加到活动场景图的组中,然后仅将组添加到runLater调用中的活动场景图中。例如:

final Group groupInSceneGraph = new Group();
Task<Void> task = new Task<Void>() {
    @Override protected Void call() throws Exception {
        final Group localGroup = new Group();
        for (int i=0; i<100; i++) {
            if (isCancelled()) break;
            final Rectangle r = new Rectangle(10, 10);
            r.setX(10 * i);

            localGroup.getChildren().add(r);
        }

        Platform.runLater(new Runnable() {
            @Override public void run() {
                groupInSceneGraph.add(localGroup);
            }
        });

        return null;
    }
};

只要对象没有附加到活动场景图,您就可以从JavaFX应用程序线程(包括加载FXML)创建和修改大多数场景图形对象。活动场景图是指当前作为场景附加到显示舞台的场景图。 (诸如WebView之类的复杂控件可能是此规则的一个例外,可能需要在JavaFX应用程序线程上创建。

您只能将JavaFX应用程序线程创建的场景图对象附加到JavaFX应用程序线程上的活动场景图(例如使用Platform.runLater())。而且,只要它们继续附加到活动场景图上,您就必须在JavaFX应用程序线程上使用它们。