Начало работы с контроллером FXMLLoader - PullRequest
0 голосов
/ 18 января 2019

Я создаю приложение JAVA FX. Можно ли получить уже открытое окно контроллера в приложении JavaFX. Например, у меня есть главный экран приложения, но когда я открыл другое окно modalWindow для ввода данных и, наконец, я ввел все данные и нажал кнопку «Сохранить». ModalWindow должно исчезнуть, а mainScreen должен быть обновлен. Я хотел сделать это, позвонив в Parents Controller из Children Controller. Но я получаю ошибки при этом. Любые другие предложения будут полезны.

Exception in thread "JavaFX Application Thread" java.lang.IllegalStateException: Location is not set.
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2428)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2403)
at controllers.ExitDialogController.setLocaleToSave(ExitDialogController.java:92)
at controllers.AddDialogDepatureController.lambda$onClick$4(AddDialogDepatureController.java:221)
at controllers.AddDialogDepatureController$$Lambda$359/1446130991.handle(Unknown Source)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8216)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)

Мой класс ChildController

 if(new HttpRequests().departPost(jsonObject))
        {
            info.setStyle("-fx-text-fill: green");
            info.setText(myResourceBundle.getString("infoSave"));
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setLocation(getClass().getResource(FxmlViews.MainScreen.mainSc));
            try
            {
                AnchorPane frame = fxmlLoader.load();
                MainScreenController mainScreenController = (MainScreenController)fxmlLoader.getController();
                mainScreenController.updateTable(myResourceBundle);
            } catch (IOException e)
            {
                e.printStackTrace();
            }
            success = true;
        }

Ответы [ 3 ]

0 голосов
/ 18 января 2019

API JavaFX позволяет получать ссылки на контроллеры только через метод FXMLLoader.load (). С экземпляром FXMLLoader вы можете сделать что-то вроде этого (как вы уже делаете :-)):

FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("MainForm.fxml"));
Parent root = (Parent) loader.load();

// as soon as the load() method has been invoked, the scene graph and the 
// controller instance are availlable:
MainFormController controller = loader.getController();

Это подразумевает:

  1. Лучше хранить ссылки на контроллеры на время строительства / время загрузки, если они понадобятся вам позже.

  2. Еще лучше: подумайте дважды, если вам нужен доступ к родительскому контроллеру из дочернего контроллера. Это может быть предупреждением о том, что с архитектурой приложения что-то не так.

  3. Если вам действительно нужна связь между дочерним контроллером и родительским контроллером, лучше сделайте это «управляемым событиями», то есть создайте наблюдаемые свойства в дочернем контроллере и попросите родительский контроллер прослушивать изменения.

Надеюсь, это поможет ...

0 голосов
/ 18 января 2019

Вот контроллер, который заменяет себя из FXML-файла. Это просто и имеет только одну кнопку, которая запускает замену, но это делает. Stage, Scene и Controller хранятся как статические переменные, чтобы хранить все в одном файле. Вы можете хранить их где угодно.

public class RootReplacingItselfController implements Initializable {
    public static RootReplacingItselfController controller = null;
    public static  Stage stage = null;
    public static Scene scene = null;
    public final static String filename = "/fxml/RootReplacingItself.fxml";

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    // TODO
    }    

    @FXML
    private void replaceme(ActionEvent event) {
        try {
           //open an additional modal Dialog 
           FXMLLoader dialogloader = new FXMLLoader(FXMLLoader.class.getResource(filename));
           Stage dialog = new Stage();

           dialog.setScene(new Scene(dialogloader.load()));
           dialog.initModality(Modality.APPLICATION_MODAL);
           dialog.initOwner(stage);
           dialog.showAndWait();

           //When the dialog is done replace myself:
           FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
            Parent node = loader.load();
            controller= loader.getController();
            scene.setRoot(node);
         } catch (IOException ex) {}
    }

    public static RootReplacingItselfController create(){

        try {
            FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
            Parent node = loader.load();
            controller= loader.getController();
            scene = new Scene(node);
            stage = new Stage();
            stage.setScene(scene);
            stage.show();
            return controller;
        } catch (IOException ex) {}
        return controller;
    }
}

И FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane id="AnchorPane" prefHeight="151.0" prefWidth="232.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/11.0.1" fx:controller="fxControls.RootReplacingItselfController">
   <children>
      <Button layoutX="76.0" layoutY="76.0" mnemonicParsing="false" onAction="#replaceme" text="replaceme" />
   </children>
</AnchorPane>

Я отредактировал его, чтобы кнопка дополнительно открывала модальный дочерний диалог. Теперь этот диалог снова создается из того же fxml. В этом модальном диалоговом окне вы можете снова нажать кнопку замены и открыть другое модальное диалоговое окно, а затем снова в этом третьем и т. Д.

Каждый раз, когда модальное диалоговое окно закрывается (диалоговое окно готово), корневой контроллер заменяется. Для всех детей, которые были созданы.

Так что теперь у вас также есть этот модальный диалог. Как только диалоговое окно закрыто, рут заменяется (и это всегда корень). И, конечно же, вы можете использовать другой FXM для диалога.

FWIW: Лично я пока не могу представить себе реальный проект, в котором я бы выбрал такой подход, поскольку он очень вероятно вызывает головную боль, когда возникает взаимодействие с другими частями приложения. Но это не мне решать.

0 голосов
/ 18 января 2019

Когда я смотрю на Stacktrace, я обнаруживаю, что он не жалуется на создание контроллера.

Он жалуется, что не может загрузить файл FXML. Это как-то не может найти это. Таким образом, он прерывается - еще до того, как он достигнет строки, в которую вы хотите загрузить контроллер (эта строка выглядит нормально):

Исключение в потоке "Поток приложений JavaFX" java.lang.IllegalStateException: местоположение не установлено. в javafx.fxml.FXMLLoader.loadImpl (FXMLLoader.java:2428) в javafx.fxml.FXMLLoader.load (FXMLLoader.java:2403)

Возможно, вы захотите взглянуть поближе на значение «FxmlViews.MainScreen.mainSc». Это очень вероятно указывает на какой-то неправильный путь.

Пример загрузки контроллера дважды:

       String filename = "yourfxm.fxml";
        FXMLLoader loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
        var node = loader.load();
        var controller = loader.getController();
        loader = new FXMLLoader(FXMLLoader.class.getResource(filename));
        node = loader.load();
        controller = loader.getController();
...