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

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

蒋昊天
2023-03-14
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()]);
    }
}

共有1个答案

衡子琪
2023-03-14

由于以下几个原因,实现相当糟糕:

singleton模式用于通过static方法访问包含相关数据/功能的实例,但该实例应包含所有相关数据作为实例字段。

但是rootstagesetup静态

public enum States {
    MAIN_MENU("../gui/MainWindow.fxml"), NEW_GAME("../gui/NewGameWindow.fxml");
    private final url;

    States(String url) {
        this.url = url;
    }
}
for(States state : States.values()) {
    FXMLLoader loader = new FXMLLoader(getClass().getResource(state.url));
    ...
}

请注意,您在此处的URL中使用..,如果您将程序打包为一个JAR,这将停止工作。

但是首先使用枚举是一个值得怀疑的决定。这样您就无法在不重新编译的情况下添加/删除场景。

如果可能的话,最好完全避免使用static。(参见为什么静态变量被认为是邪恶的?)。

控制器的超类

public class BaseController {
    protected SceneManager sceneManager;
    void setSceneManager(SceneManager sceneManager) { // if SceneManager and BaseController are in different packages, change visibility
        this.sceneManager = sceneManager;
    }
}
FXMLLoader loader = ...
Pane pane = loader.load();
BaseController controller = loader.getController();
controller.setSceneManager(this);

为了简单起见,使用url作为场景的标识符,可以改进实现:

public class SceneManager {
    
    private final Stage rootStage;

    public SceneManager(Stage rootStage) {
        if (rootStage == null) {
            throw new IllegalArgumentException();
        }
        this.rootStage = rootStage;
    }

    private final Map<String, Scene> scenes = new HashMap<>();

    public void switchScene(String url) {
        Scene scene = scenes.computeIfAbsent(url, u -> {
            FXMLLoader loader = new FXMLLoader(getClass().getResource(u));
            try {
                Pane p = loader.load();
                BaseController controller = loader.getController();
                controller.setSceneManager(this);
                return new Scene(p);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        });
        rootStage.setScene(scene);
    }
}

这允许您

    null
 类似资料:
  • 当我运行该程序时,它加载第一个和第二个fxml,但继续显示第一个fxml(当它应该显示第二个时)。我可以使用相同代码的输入(即按钮)从第一个屏幕到第二个屏幕。 我尝试使用不同的加载器,每次设置场景时都使用。show()方法,但这些(或它们的许多变体)都不起作用。 我在调用方法时进行打印--这就是我确定它们被加载的方式(printline): null 相反,输出(在调试中,如果stage.show

  • 我创建了一个游戏,我想给它添加一个开始屏幕,我使用FXML添加了它,还添加了两个按钮(开始和退出)。 按下开始按钮后,我希望游戏加载场景并切换到游戏开始。我对如何做有一个粗略的想法,但我有点挣扎,因为我的SampleController类不知道如何启动游戏等,因为所有代码(以及加载初始开始菜单的代码)都在我的主类中,所以我尝试了这样的事情: 我尝试使用一个函数来切换场景,但它不起作用,也试图使用获

  • 我看了很多页,试图找出如何切换场景,但都没有成功。 我有一个计算器,我的目标是选择一个菜单选项来更改计算器(即:基础和科学)。现在我只是在测试,所以这里是我到目前为止与这个问题相关的代码(我使用的是场景生成器): 编辑我已经尝试了很多东西。不管怎样,我总是得到这个NullPointerException。我有一种感觉,这可能与在场景生成器中设置某些内容有关,但我就是找不到答案 工作代码: 我使用下

  • 我想在2个不同的场景之间切换: 问题是当我从场景2切换到场景1时,场景1中加载的所有图像都不在那里(这很明显,因为我正在创建一个新场景,而不是“加载”场景1。 当我从场景2切换到场景1时,有什么方法可以保留已经加载的图像吗? 场景1

  • 我在class1中创建了一个场景,然后在Class2中创建了一个scene2。如何在两者之间切换? 这是第二节课,我有另一个场景。

  • 我正在将游戏的场景从启动页面切换到游戏,如果我单独玩,游戏场景效果很好,但当我从启动页面切换后玩游戏时,游戏会冻结一段时间。 是什么原因导致我的主游戏在切换场景后冻结了一段时间(一段时间后再次启动),我确信游戏在没有切换场景的情况下运行良好。 程序的控制流程是——从播放器开始,startuppage场景被加载到舞台,如果按下播放按钮,则在主控制器中,场景被切换到我的游戏场景。问题只会在切换场景时出