当前位置: 首页 > 知识库问答 >
问题:

在后台加载JavaFX GUI

盖诚
2023-03-14

我目前正在尝试为我的程序创建一个启动屏幕,因为启动需要一些时间。问题是创建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屏幕,而无需任何真正的并发。代码如下所示:

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秒的改进。但是我仍然想知道是否有办法在后台线程上创建图形用户界面。

编辑:

按照建议,我还尝试在“任务”中使用“RunLater”。这样,我可以创建 GUI 并显示初始屏幕,但我无法更新进度条和进度标签,因为 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个答案

慕容越泽
2023-03-14

请参阅标题为“修改场景图的任务”的任务文档,其中提供了一个示例:

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应用程序线程上使用它们。

 类似资料:
  • 我正在尝试执行某些操作,例如创建一个表,并在应用程序的初始启动时插入720行,这需要一些时间,所以我决定在后台运行它。请参见下面的代码, 主要活动。Java语言 当我运行它时,它显示logcat中的以下错误, 请帮忙! 仅供参考:整个Logcat

  • 我原以为这是一个简单的问题,但我很难找到答案。我有一个与JavaFX场景对象关联的ImageView对象,我想从磁盘加载大图像,并使用ImageView一个接一个地显示它们。我一直在努力寻找一种好方法来反复检查图像对象,当它在后台加载完成时,将其设置为ImageView,然后开始加载新的图像对象。我提出的代码(如下)有时有效,有时无效。我很确定我在JavaFX和线程方面遇到了问题。它有时加载第一个

  • 我正在创建一个应用程序,其中我从服务器下载一些数据。在后台运行时,我希望连接应该继续运行,以便可以下载数据。我知道在app里有方法 当应用程序进入后台时调用。但由于连接是在viewController中创建的,如何在appDelegate中管理它 还有其他方法可以做到这一点吗?我已经通过了这个链接,但是有一个简单的实现方法吗?

  • 我最初的fxml(例如)有很多功能,因此完全加载需要大量时间。因此,为了避免程序启动和fxml加载之间的时间间隔,我引入了另外一个fxml()和一个gif图像,该图像应该在加载主fxml时出现。问题是我的加载器中的gif图像。fxml不会移动,就像程序中的挂起一样,直到home.fxml被完全加载。为了避免这种情况,我将home.fxml加载移到一个线程中,如下代码所示。 但是在这段代码之后,我的

  • 我试图在一个网站上获得一个内容列表(若有人感兴趣的话,这一个)。布局最近发生了变化,现在他们不会一次加载所有内容,而是使用magic(可能是js)。我目前正在使用JSoup分析HTML,但我愿意接受建议。 这就是我得到的: 实现此目的的代码: 这是我想看到的(复制从检查元素后,页面加载了所有的内容): 谷歌帮不了什么忙,因为与微调器等相关的所有内容都与javascript有关。 解决方案: 由于J

  • 问题内容: 我在新应用中使用Alamofire(基于Alamofire的下载管理器示例),我需要一些有关使用后台会话下载文件的说明。我需要重写SessionDelegate才能使其正常工作?还是只是? 通常,使用Alamofire在后台处理下载的步骤是什么?以及如何处理我的应用程序重新启动,下载量不断增加的情况。 问题答案: 更新资料 基于这个惊人的教程,我整理了一个GitHub上可用的示例项目。