猿问

通过 FXML 管理 JavaFX 中的场景切换(性能问题)

我正在玩 JavaFX 和 FXML 加载的各种场景。所以我有了写一个处理场景切换的管理器的想法。


到目前为止一切正常,但我不确定这是否是一个好的实现。


import java.io.IOException;

import javafx.fxml.FXMLLoader;

import javafx.scene.Scene;

import javafx.scene.layout.Pane;

import javafx.stage.Stage;


public class SceneManager {

    private static final String[] fxmlFiles = {"../gui/MainWindow.fxml", "../gui/NewGameWindow.fxml"};

    private static SceneManager instance = null;

    private static Stage rootStage = null;


    private FXMLLoader[] loadedFxml;

    private Pane[] loadedPanes;

    private Scene[] scenes;


    public enum States {

        MAIN_MENU, NEW_GAME;

    }


    private SceneManager() {

        try {

            this.loadedFxml = new FXMLLoader[States.values().length];

            this.loadedPanes = new Pane[States.values().length];

            this.scenes = new Scene[States.values().length];


            for(int i = 0; i < fxmlFiles.length; i++) {

                loadedFxml[i] = new FXMLLoader(getClass().getResource(fxmlFiles[i]));

                loadedPanes[i] = loadedFxml[i].load();

                scenes[i] = new Scene(loadedPanes[i]);

            }


            rootStage.setScene(scenes[0]);

            rootStage.setResizable(false);

            rootStage.show();

        } catch(IOException e) {

            e.printStackTrace();

        }

    }


    public static SceneManager getInstance() {

        if(instance == null) {

            instance = new SceneManager();

        }

        return instance;

    }


    public static void setUp(Stage stage) {

        SceneManager.rootStage = stage;

    }


    public void switchScene(States state) {

        rootStage.setScene(scenes[state.ordinal()]);

    }

}

所以我打算做的是,通过加载器加载 FXML,将其分配给一个窗格,创建所有场景。


然后我将一个场景设置为它的起始场景,并通过控制器中的 getInstance().switchScene() 方法完成剩下的工作。


它运作良好,但我不确定这是否是一个好方法。


慕标琳琳
浏览 255回答 1
1回答

撒科打诨

恕我直言,由于以下几个原因,实现非常糟糕:单例模式没有正确实现单例模式用于通过static方法访问包含相关数据/功能的实例,但此实例应包含所有相关数据作为实例字段。rootStage和setUp是static虽然。您将数据存储在不再需要的字段中您永远不会从循环中loadedFxml或loadedPanes在循环外读取。您可以改为在循环体中创建局部变量。一切都立即加载对于几个小场景,这可能没有太大区别,但是随着您添加越来越多的场景,这将增加启动时间。考虑延迟加载场景。场景数据保存在不同的数据结构中不是什么大问题,但它使类更难维护。该enum数据的一部分用于创建存储/接入场景fxmlFiles中包含的另一半。每次添加/删除场景时,您都需要更新代码的两部分。在这种情况下,最好将 url 数据存储在枚举本身中:public enum States {&nbsp; &nbsp; MAIN_MENU("../gui/MainWindow.fxml"), NEW_GAME("../gui/NewGameWindow.fxml");&nbsp; &nbsp; private final url;&nbsp; &nbsp; States(String url) {&nbsp; &nbsp; &nbsp; &nbsp; this.url = url;&nbsp; &nbsp; }}for(States state : States.values()) {&nbsp; &nbsp; FXMLLoader loader = new FXMLLoader(getClass().getResource(state.url));&nbsp; &nbsp; ...}请注意..,如果您将程序打包为 jar ,您在此处使用的 url 将停止工作。但是首先使用 aenum是一个有问题的决定。这样,您将无法在不重新编译的情况下添加/删除场景。使用static似乎根本没有必要static如果可能,最好完全避免使用。(请参阅为什么静态变量被认为是邪恶的?)。如果我假设您只使用SceneManager它加载的场景中的类(并用于显示初始场景)是正确的,那么将SceneManager实例传递给场景的控制器以避免SceneManager.getInstance在这些类中使用并不难(请参阅传递参数 JavaFX FXML ):控制器的超类public class BaseController {&nbsp; &nbsp; protected SceneManager sceneManager;&nbsp; &nbsp; void setSceneManager(SceneManager sceneManager) { // if SceneManager and BaseController are in different packages, change visibility&nbsp; &nbsp; &nbsp; &nbsp; this.sceneManager = sceneManager;&nbsp; &nbsp; }}FXMLLoader loader = ...Pane pane = loader.load();BaseController controller = loader.getController();controller.setSceneManager(this);为简单起见,使用 url 作为场景的标识符,您可以改进实现:public class SceneManager {&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; private final Stage rootStage;&nbsp; &nbsp; public SceneManager(Stage rootStage) {&nbsp; &nbsp; &nbsp; &nbsp; if (rootStage == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new IllegalArgumentException();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; this.rootStage = rootStage;&nbsp; &nbsp; }&nbsp; &nbsp; private final Map<String, Scene> scenes = new HashMap<>();&nbsp; &nbsp; public void switchScene(String url) {&nbsp; &nbsp; &nbsp; &nbsp; Scene scene = scenes.computeIfAbsent(url, u -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FXMLLoader loader = new FXMLLoader(getClass().getResource(u));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Pane p = loader.load();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BaseController controller = loader.getController();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; controller.setSceneManager(this);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new Scene(p);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } catch (IOException ex) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new RuntimeException(ex);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; &nbsp; &nbsp; rootStage.setScene(scene);&nbsp; &nbsp; }}这使您可以为不同阶段创建不同的经理首先在需要时加载场景动态添加更多场景防止switchScene被调用但阶段是的状态null简化对SceneManagerin 控制器类的访问sceneManager.switchSceneSceneManager&nbsp;在程序完成之前可能可用于垃圾收集,因为没有对它的静态引用。
随时随地看视频慕课网APP

相关分类

Java
我要回答