Управление переключением сцен в JavaFX через FXML (вопрос производительности) - PullRequest
0 голосов
/ 22 октября 2018

Я играю с 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 Ответ

0 голосов
/ 22 октября 2018

Реализация imho довольно плохая по нескольким причинам:

Шаблон синглтона не реализован должным образом

Шаблон синглтона используется для доступа к экземпляру, содержащему соответствующие данные / функции, через 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));
    ...
}

Обратите внимание, что вы используете .. в своих URL, которые перестают работать, если вы упакуетепрограмма как баночка.

Но использование enum в первую очередь является сомнительным решением.Таким образом, вы не сможете добавлять / удалять сцены без перекомпиляции.

Использование 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
  • упрощает доступ к SceneManager в классах контроллера до sceneManager.switchScene
  • SceneManager, возможно, доступного для сборки мусора до завершения программы, поскольку существуетнет статической ссылки на него.
...