亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

通過 FXML 管理 JavaFX 中的場景切換(性能問題)

通過 FXML 管理 JavaFX 中的場景切換(性能問題)

慕標琳琳 2021-12-01 16:53:37
我正在玩 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() 方法完成剩下的工作。它運作良好,但我不確定這是否是一個好方法。
查看完整描述

1 回答

?
撒科打諢

TA貢獻1934條經驗 獲得超2個贊

恕我直言,由于以下幾個原因,實現非常糟糕:


單例模式沒有正確實現

單例模式用于通過static方法訪問包含相關數據/功能的實例,但此實例應包含所有相關數據作為實例字段。


rootStage和setUp是static雖然。


您將數據存儲在不再需要的字段中

您永遠不會從循環中loadedFxml或loadedPanes在循環外讀取。您可以改為在循環體中創建局部變量。


一切都立即加載

對于幾個小場景,這可能沒有太大區別,但是隨著您添加越來越多的場景,這將增加啟動時間??紤]延遲加載場景。


場景數據保存在不同的數據結構中

不是什么大問題,但它使類更難維護。該enum數據的一部分用于創建存儲/接入場景fxmlFiles中包含的另一半。每次添加/刪除場景時,您都需要更新代碼的兩部分。在這種情況下,最好將 url 數據存儲在枚舉本身中:


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

    ...

}

請注意..,如果您將程序打包為 jar ,您在此處使用的 url 將停止工作。


但是首先使用 aenum是一個有問題的決定。這樣,您將無法在不重新編譯的情況下添加/刪除場景。


使用static似乎根本沒有必要

static如果可能,最好完全避免使用。(請參閱為什么靜態變量被認為是邪惡的?)。


如果我假設您只使用SceneManager它加載的場景中的類(并用于顯示初始場景)是正確的,那么將SceneManager實例傳遞給場景的控制器以避免SceneManager.getInstance在這些類中使用并不難(請參閱傳遞參數 JavaFX FXML ):


控制器的超類


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

    }

}

這使您可以

  • 為不同階段創建不同的經理

  • 首先在需要時加載場景

  • 動態添加更多場景

  • 防止switchScene被調用但階段是的狀態null

  • 簡化對SceneManagerin 控制器類的訪問sceneManager.switchScene

  • SceneManager 在程序完成之前可能可用于垃圾收集,因為沒有對它的靜態引用。


查看完整回答
反對 回復 2021-12-01
  • 1 回答
  • 0 關注
  • 299 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號