将fxml加载为后台进程-Javafx [英] Load fxml as background process - Javafx

查看:160
本文介绍了将fxml加载为后台进程-Javafx的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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图像没有移动).如果将fxml加载到Platform.runLater()之外,则会出现异常Not on FX Thread.

我也很厌倦使用Task(),但是如果尝试将fxml加载到Platform.runLater()之外,则gif图像会移动,但是fxml不会在后台加载.

任何人都可以帮助我,告诉我如何更正代码,以便我的fxml在后台加载而不会干扰前台进程.

解决方案

使用Task.您需要安排创建场景并在FX Application Thread上更新舞台.最干净的方法是使用Task<Parent>:

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:

 <?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方法):

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:

 <?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:

import javafx.fxml.FXML;
import javafx.scene.control.Label;

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

主要应用程序:

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所需的五秒钟内在文本字段中键入内容,因此UI保持响应状态.

My initial fxml(say home.fxml) has a lot of functionalities, hence it takes a lot of time to load completely. So to avoid the time gap between program start and fxml loading I introduced one more fxml(say loader.fxml) with a gif image which should appear while the main fxml is loading. The problem is the gif image in my loader.fxml is not moving as in the program is hanging until the home.fxml is loaded completely. To avoid this I moved the home.fxml loading into a thread as shown in below code.

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);
    }

}

But after this code also my program is hanging(The gif image is not moving). If I load the fxml outside the Platform.runLater(), I get the exception Not on FX Thread.

I also tired using Task() but from that the gif image is moving but the fxml is not loading in the background, if I try to load the fxml outside Platform.runLater().

Can anyone please help me and tell me how can I correct the code so that my fxml loads in the background without disturbing the foreground process.

解决方案

Use a Task. You need to arrange to create the scene and update the stage on the FX Application Thread. The cleanest way is to use a Task<Parent>:

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();


Here is a SSCCE using this technique:

main.fxml:

<?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 (uses the Task approach shown above):

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:

<?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:

import javafx.fxml.FXML;
import javafx.scene.control.Label;

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

Main application:

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);
    }
}

Notice that after pressing the button, you can still type in the text field during the five seconds it takes to "load" the FXML, so the UI is remaining responsive.

这篇关于将fxml加载为后台进程-Javafx的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆