JavaFX: Як отримати етап від контролера під час ініціалізації?


91

Я хочу обробляти події сцени (тобто приховування) від мого класу контролера. Тому все, що мені потрібно зробити, це додати слухача через

((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 (), і коли він не стає нульовим, я отримую сцену, додаю до неї .windowProperty () мій! Блін! обробка слухачів, яку я нарешті отримую на сцені. А все закінчується налаштуванням бажаних слухачів на сценічні події. Думаю, слухачів забагато. Це єдиний спосіб вирішити мою проблему?

Відповіді:


119

Ви можете отримати екземпляр контролера з 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

1
Думаєте, вам не вистачає акторського складу? Батьківський корінь = (Батьківський) loader.load ();
Paul Eden

12
Це справді найкращий спосіб це зробити? Структура JavaFX нічого не забезпечує для архівування цього?
Hannes

1
З якихось причин це не працює, якщо ви використовуєте конструктор без параметрів і передаєте URL-адресу load()методу. (Крім того, javadoc на getControllerзвучить так, ніби він повинен бути включений setController.)
Bombe

4
@Bombe це тому, що метод load () із параметром URL є статичним методом, який не встановлює нічого в екземплярі, до якого ви його викликаєте.
zhujik

@ Себастьян, справді. Моє ліжко.
Бомбе

112

Все, що вам потрібно, це надати AnchorPaneпосвідчення особи, і тоді ви зможете отримати Stageце.

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

Звідси ви можете додати те, Listenerщо вам потрібно.

Змінити: Як зазначено нижче EarthMind, це не повинен бути AnchorPaneелемент; це може бути будь-який елемент, який ви визначили.


2
Короткий і солодкий. Дякую
Седрік

7
Зверніть увагу , що elementможе бути будь-який елемент з fx:idв цьому вікні: Stage stage = (Stage) element.getScene().getWindow();. Наприклад, якщо у вас у вікні є лише символ Buttonз fx:id, використовуйте його, щоб отримати сцену.
EarthMind

29
getScene()все ще повертається null.
m0skit0

3
Це відповідь, акуратно!
дестан

12
До завершення ініціалізації (наприклад, у методі ініціалізації контролера) це не спрацює, оскільки getScene () повертає null. Оригінальний плакат передбачає, що це його ситуація. У цьому випадку альтернатива 1, подана Utku Özdemir нижче, є кращою. Якщо вам не потрібна сцена, тоді достатньо одного слухача, я використовую це в initialize (), щоб зробити ImageView розтяжку:bgimage.sceneProperty().addListener((observableScene, oldScene, newScene) -> { if (oldScene == null && newScene != null) { bgimage.fitWidthProperty().bind(newScene.widthProperty()); ... } });
Джорджі

32

Я знаю, що це не відповідь, яку ви хочете, але запропоновані рішення IMO не є хорошими (і ваш власний шлях). Чому? Оскільки вони залежать від стану програми. У JavaFX елемент керування, сцена та сцена не залежать один від одного. Це означає, що елемент керування може жити без додавання до сцени, а сцена може існувати без прикріплення до сцени. І тоді, в момент миттєвого t1, керування може приєднатись до сцени, і миттєво t2 ця сцена може бути додана до сцени (і це пояснює, чому вони є спостережуваними властивостями один одного).

Отже, підхід, який пропонує отримати посилання на контролер та викликати метод, передавши йому етап, додає стан до вашої програми. Це означає, що вам потрібно викликати цей метод у потрібний момент, відразу після створення етапу. Іншими словами, вам потрібно виконати порядок зараз: 1- Створити етап - Передати цей створений етап контролеру за допомогою методу.

Ви не можете (або не повинні) змінювати цей порядок у цьому підході. Отже, ви втратили апатрид. А в програмному забезпеченні загалом держава - це зло. В ідеалі методи не повинні вимагати жодного замовлення викликів.

То яке правильне рішення? Є дві альтернативи:

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

1
Це найкоротше посилання на JavaFX на планеті !! Дякую, Утку!
Арам Паронікян

Цікавий погляд на це. Дякую :)
Utku Özdemir

Я намагаюся використовувати цей підхід для заповнення TableView і показування ProgressIndicator в центрі відносно TableView, але виявляється, що всередині подій значення getX () і getY () не однакові, як якщо б ви використовували після всієї ініціалізації закінчення процесу (всередині події клацання на кнопці) або для сцени, і для сцени, навіть для етапу getX () та getY () повернення NaN, будь-яка ідея?
leobelizquierdo

@leobelizquierdo, на даний момент проблема з getY (), ти знайшов рішення?
JonasAnon 20.03.17

якщо я додаю paneдо сцени, яка вже прикріплена до сцени, це не спрацює, так? Чи не слід перевіряти scenePropertyслухача і додавати stagePropertyслухача, лише якщо сцена все ще нульова? В іншому випадку просто зроби те, що мені потрібно відразу?
завтра

14

Найпростіший спосіб отримати об'єкт сцени в контролері:

  1. Додайте додатковий метод у власний створений клас контролера, наприклад (це буде метод сеттера для встановлення етапу в класі контролера),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
  2. Отримайте контролер у методі запуску та встановіть етап

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


Це було переможцем для мене. Відповідь Роберта Мартіна спочатку спрацював, але потім почав видавати помилку, яку я вирішив, отримавши stageтаку.
Даммел

1

Призначте fx: id або оголосіть змінну будь-якому вузлу: anchorpane, button і т. Д. Потім додайте до нього обробник подій, і всередині цього обробника подій вставте вказаний нижче код:

Stage stage = (Stage)((Node)((EventObject) eventVariable).getSource()).getScene().getWindow();

Сподіваюся, це працює для вас !!


1

Platform.runLater працює, щоб запобігти виконанню до завершення ініціалізації. У цьому випадку я хочу оновлювати подання списку кожного разу, коли я змінюю розмір ширини вікна.

Platform.runLater(() -> {
    ((Stage) listView.getScene().getWindow()).widthProperty().addListener((obs, oldVal, newVal) -> {
        listView.refresh();
    });
});

у вашому випадку

Platform.runLater(()->{
    ((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
});

-3

Ви можете отримати node.getScene, якщо не зателефонуватиPlatform.runLater , результат - нульове значення.

приклад нульового значення:

node.getScene();

Приклад немає нульового значення:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.