JavaFX: Как получить этап от контроллера во время инициализации?

Я хочу обрабатывать события этапа (т.е. скрывать) из моего класса контроллера. Поэтому мне нужно только добавить слушателя через

((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);

но проблема в том, что инициализация начинается сразу после

Parent root = FXMLLoader.load(getClass().getResource("MyGui.fxml"));

и до

Scene scene = new Scene(root);
stage.setScene(scene);

таким образом .getScene() возвращает null.

Единственное обходное решение, которое я нашел сам, состоит в том, чтобы добавить слушателя к myPane.sceneProperty(), и когда он станет не null, я получаю сцену, добавляю к ней .windowProperty() my! черт! обработчик слушателя, который я, наконец, извлекаю. И все это заканчивается настройкой желаемых слушателей на сценические события. Я думаю, что слишком много слушателей. Это единственный способ решить мою проблему?

Ответ 1

Вы можете получить экземпляр контроллера из FXMLLoader после инициализации с помощью getController(), но вам нужно вместо этого установить инстанцирование FXMLLoader вместо использования статических методов.

Я передал бы сцену после вызова load() непосредственно на контроллер:

FXMLLoader loader = new FXMLLoader(getClass().getResource("MyGui.fxml"));
Parent root = (Parent)loader.load();
MyController controller = (MyController)loader.getController();
controller.setStageAndSetupListeners(stage); // or what you want to do

Ответ 2

Все, что вам нужно, это дать идентификатору AnchorPane, а затем вы можете получить Stage от этого.

@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();

Здесь вы можете добавить Listener, который вам нужен.

Изменить: Как указано EarthMind ниже, он не должен быть элементом AnchorPane; это может быть любой элемент, который вы определили.

Ответ 3

Я знаю, что это не тот ответ, который вы хотите, но ИМО предлагаемые решения не очень хороши (и ваш собственный путь). Зачем? Потому что они зависят от состояния приложения. В JavaFX элемент управления, сцена и сцена не зависят друг от друга. Это означает, что управление может жить без добавления в сцену, и сцена может существовать без привязки к сцене. И затем, в момент времени t1, управление может привязываться к сцене и в момент t2 эта сцена может быть добавлена ​​на сцену (и это объясняет, почему они являются наблюдаемыми свойствами друг друга).

Итак, подход, который предлагает получить ссылку на контроллер и вызвать метод, передавая ему этап, добавляет состояние в ваше приложение. Это означает, что вам нужно вызвать этот метод в нужный момент, сразу после создания сцены. Другими словами, вам нужно выполнить заказ сейчас: 1- Создать сцену 2- Передайте этот созданный этап контроллеру с помощью метода.

Вы не можете (или не должны) изменять этот порядок в этом подходе. Таким образом, вы потеряли безгражданство. И в программном обеспечении, как правило, состояние - это зло. В идеале, методы не должны требовать порядка вызова.

Итак, какое правильное решение? Существуют две альтернативы:

1- Ваш подход, в функции прослушивания контроллера, чтобы получить сцену. Я думаю, что это правильный подход. Вот так:

pane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {
    if (oldScene == null && newScene != null) {
        // scene is set for the first time. Now its the time to listen stage changes.
        newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> {
            if (oldWindow == null && newWindow != null) {
                // stage is set. now is the right time to do whatever we need to the stage in the controller.
                ((Stage) newWindow).maximizedProperty().addListener((a, b, c) -> {
                    if (c) {
                        System.out.println("I am maximized!");
                    }
                });
            }
        });
    }
});

2- Вы делаете то, что вам нужно, создавая Stage (и это не то, что вы хотите):

Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
            if (c) {
                System.out.println("I am maximized!");
            }
        });
stage.setScene(someScene);
...

Ответ 4

Самый простой способ получить объект stage в контроллере:

  • Добавьте дополнительный метод в собственный созданный класс контроллера вроде (это будет метод setter, чтобы установить этап в классе контроллера),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
  • Получить контроллер в методе запуска и установить этап

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
    
  • Теперь вы можете получить доступ к сцене в контроллере

Ответ 5

Puedes llamarlo de de la siguiente tomando un nodo del fxml.
you can get with node. getScene, if you dont call from Platform.runLater, the result is a null value.

example null value:

node.getScene();

example no null value:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});

Enjoy