首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >加载fxml作为后台进程- Javafx

加载fxml作为后台进程- Javafx
EN

Stack Overflow用户
提问于 2016-01-19 09:58:37
回答 3查看 4K关注 0票数 1

我最初的fxml(比如home.fxml)有很多功能,因此它需要大量的时间才能完全加载。因此,为了避免程序启动和fxml加载之间的时间间隔,我又引入了一个带有gif映像的fxml (例如loader.fxml),该映像应该在主fxml加载时出现。问题是,在我的loader.fxml中的gif映像没有移动,因为在程序中挂起,直到home.fxml被完全加载。为了避免这种情况,我将home.fxml加载移到一个线程中,如下代码所示。

代码语言:javascript
运行
复制
public class UATReportGeneration extends Application {

    private static Stage mainStage;

    @Override
    public void start(Stage stage) {
        Parent loaderRoot = null;
        try {
            loaderRoot = FXMLLoader.load(getClass().getResource("/uatreportgeneration/fxml/Loader.fxml"));
        } catch (IOException ex) {
            Logger.getLogger(UATReportGeneration.class.getName()).log(Level.SEVERE, null, ex);
        }
        Scene loadScene = new Scene(loaderRoot);
        stage.setScene(loadScene);
        stage.initStyle(StageStyle.UNDECORATED);
        stage.getIcons().add(new Image(this.getClass().getResourceAsStream("/uatreportgeneration/Images/logo.png")));

        stage.show();


        mainStage = new Stage(StageStyle.UNDECORATED);
        mainStage.setTitle("Upgrade Analysis");
        mainStage.getIcons().add(new Image(this.getClass().getResourceAsStream("/uatreportgeneration/Images/logo.png")));
        setStage(mainStage);

        new Thread(() -> {
            Platform.runLater(() -> {
                try {
                    FXMLLoader loader = new FXMLLoader();
                    Parent root = loader.load(getClass().getResource("/uatreportgeneration/fxml/Home.fxml"));

                    Scene scene = new Scene(root);
                    mainStage.setScene(scene);
                    mainStage.show();
                    stage.hide();
                    System.out.println("Stage showing");
                    // Get current screen of the stage
                    ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight()));
                    // Change stage properties
                    Rectangle2D bounds = screens.get(0).getVisualBounds();
                    mainStage.setX(bounds.getMinX());
                    mainStage.setY(bounds.getMinY());
                    mainStage.setWidth(bounds.getWidth());
                    mainStage.setHeight(bounds.getHeight());
                    System.out.println("thread complete");
                } catch (IOException ex) {
                    Logger.getLogger(UATReportGeneration.class.getName()).log(Level.SEVERE, null, ex);
                }

            });
        }).start();

    }

    public static Stage getStage() {
        return mainStage;
    }

    public static void setStage(Stage stage) {
        mainStage = stage;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        launch(args);
    }

}

但是在这段代码之后,我的程序也挂起了( gif图像没有移动)。如果我在Platform.runLater()之外加载fxml,就会得到异常Not on FX Thread

我也厌倦了使用Task(),但是如果我试图在Platform.runLater()之外加载fxml,那么gif映像就会移动,但是fxml不会在后台加载。

请任何人帮助我,并告诉我如何纠正代码,这样我的fxml就可以在后台加载,而不会干扰前台进程。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-01-19 14:04:07

使用Task。您需要安排创建场景和更新舞台上的FX应用线程。最干净的方法是使用Task<Parent>

代码语言:javascript
运行
复制
Task<Parent> loadTask = new Task<Parent>() {
    @Override
    public Parent call() throws IOException {
        FXMLLoader loader = new FXMLLoader();
        Parent root = loader.load(getClass().getResource("/uatreportgeneration/fxml/Home.fxml"));
        return root ;
    }
};

loadTask.setOnSucceeded(e -> {
    Scene scene = new Scene(loadTask.getValue());

    mainStage.setScene(scene);
    mainStage.show();
    stage.hide();
    System.out.println("Stage showing");
    // Get current screen of the stage
    ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight()));
    // Change stage properties
    Rectangle2D bounds = screens.get(0).getVisualBounds();
    mainStage.setX(bounds.getMinX());
    mainStage.setY(bounds.getMinY());
    mainStage.setWidth(bounds.getWidth());
    mainStage.setHeight(bounds.getHeight());
    System.out.println("thread complete");
});

loadTask.setOnFailed(e -> loadTask.getException().printStackTrace());

Thread thread = new Thread(loadTask);
thread.start();

下面是使用此技术的SSCCE:

main.fxml:

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>

<VBox spacing="10" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
    <padding>
        <Insets top="24" left="24" right="24" bottom="24"/>
    </padding>
    <TextField />
    <Button fx:id="button" text="Show Window" onAction="#showWindow"/>
</VBox>

MainController (使用上面所示的Task方法):

代码语言:javascript
运行
复制
import java.io.IOException;

import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class MainController {
    @FXML
    private Button button ;
    @FXML
    private void showWindow() {
        Task<Parent> loadTask = new Task<Parent>() {
            @Override
            public Parent call() throws IOException, InterruptedException {

                // simulate long-loading process:
                Thread.sleep(5000);

                FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));
                Parent root = loader.load();
                return root ;
            }
        };

        loadTask.setOnSucceeded(e -> {
            Scene scene = new Scene(loadTask.getValue());
            Stage stage = new Stage();
            stage.initOwner(button.getScene().getWindow());
            stage.setScene(scene);
            stage.show();
        });

        loadTask.setOnFailed(e -> loadTask.getException().printStackTrace());

        Thread thread = new Thread(loadTask);
        thread.start();
    }
}

test.fxml:

代码语言:javascript
运行
复制
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="TestController">
    <padding>
        <Insets top="24" left="24" right="24" bottom="24"/>
    </padding>
    <center>
        <Label fx:id="label" text="This is a new window"/>
    </center>
    <bottom>
        <Button text="OK" onAction="#closeWindow" BorderPane.alignment="CENTER">
            <BorderPane.margin>
                <Insets top="5" bottom="5" left="5" right="5"/>
            </BorderPane.margin>
        </Button>
    </bottom>
</BorderPane>

TestController:

代码语言:javascript
运行
复制
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class TestController {
    @FXML
    private Label label ;
    @FXML
    private void closeWindow() {
        label.getScene().getWindow().hide();
    }
}

主要应用:

代码语言:javascript
运行
复制
import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        primaryStage.setScene(new Scene(FXMLLoader.load(getClass().getResource("main.fxml"))));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

注意,按下按钮后,仍然可以在“加载”FXML所需的5秒内输入文本字段,这样UI才能保持响应。

票数 5
EN

Stack Overflow用户

发布于 2016-01-19 11:39:01

您使用Task的方法已经是正确的。您只是少了一点:只是缺少了另一个更新UI的Platform#invokeLater()

代码语言:javascript
运行
复制
    new Thread(new Task() {

        @Override
        protected Object call() throws Exception {
            // Simulating long loading
            Thread.sleep(5000);

            FXMLLoader loader = new FXMLLoader(getClass().getResource("home.fxml"));
            Parent root = loader.load();

            Scene scene = new Scene(root);

            // Updating the UI requires another Platform.runLater()
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    mainStage.setScene(scene);
                    mainStage.show();
                    stage.hide();
                    System.out.println("Stage showing");
                    // Get current screen of the stage
                    ObservableList<Screen> screens = Screen.getScreensForRectangle(new Rectangle2D(mainStage.getX(), mainStage.getY(), mainStage.getWidth(), mainStage.getHeight()));
                    // Change stage properties
                    Rectangle2D bounds = screens.get(0).getVisualBounds();
                    mainStage.setX(bounds.getMinX());
                    mainStage.setY(bounds.getMinY());
                    mainStage.setWidth(bounds.getWidth());
                    mainStage.setHeight(bounds.getHeight());
                    System.out.println("thread complete");
                }
            });
            return null;
        }
    }).start();
票数 0
EN

Stack Overflow用户

发布于 2016-01-21 06:13:49

除了@James_D的答案。正如他所提到的,舞台和场景不应该添加在后台线程中,它们应该只在FX主线程中添加。

我在我的home.fxml中添加了home.fxml,这只不过是一个PopupWindow。因此,它作为一个新的阶段出现在背景线上。因此,它抛出了IllegalStateException。从fxml中删除tooltips之后,fxml能够作为后台进程加载,因为该线程中没有创建阶段。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34873673

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档