【问题标题】:Getting opened FXMLLoader's Controller打开 FXMLLoader 的控制器
【发布时间】:2019-01-18 12:20:22
【问题描述】:

我正在创建 JAVA FX 应用程序。是否有可能在 JavaFX 应用程序中获得控制器已经打开的窗口。例如,我有应用程序的 mainScreen,但是当我打开其他 modalWindow 输入数据时,最后我输入了所有数据并单击了保存按钮。 ModalWindow 应该消失并且 mainScreen 应该被刷新。我想做它调用父母控制器从儿童控制器调用它。但是我在执行此操作时遇到错误。任何其他建议都会很有用。

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

【问题讨论】:

  • 尝试将 fxmlLoader.setLocation ... 的参数更改为 ... .getResource(/FXMLViews/MainScreen/mainSc.fxml):

标签: java intellij-idea javafx fxml fxmlloader


【解决方案1】:

JavaFX API 只允许您通过 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. 如果您确实需要子控制器和父控制器之间的通信,最好“事件驱动”,即在子控制器中创建可观察的属性并让父控制器监听变化。

    李>

希望对你有帮助……

【讨论】:

  • 谢谢先生的回复。但它仍然给出以前的错误。无法再次加载 Parent Fxml,因为它已经打开了
【解决方案2】:

当我查看 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();

【讨论】:

  • 谢谢先生的回答。是的,你在这里完全正确。它无法加载已经打开的 FXml。我可以在这里做些什么来正确加载它并重新打开它或者你会向我建议什么?
  • 我在上面编辑了我的答案并添加了一些可以正常加载的代码。多次打开一个 fxml 文件根本不成问题!另外,您的代码抱怨说它无法find fxml 文件。它说,文件的位置是错误的。它看错了地方。
  • 也许我理解得更好,如果你能告诉我是什么让你相信,错误的原因是文件已经打开或者控制器已经加载? (我从你的帖子中看不到)
【解决方案3】:

这是一个从 FXML 文件中替换自身的控制器。 它很简单,只有一个按钮可以触发更换,但确实如此。 舞台、场景和控制器存储为静态变量,以将所有内容保存在一个文件中。您当然可以将它们存储在您想要的任何其他地方。

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:就个人而言,目前我无法想象一个真正的项目会选择这样一种方法,因为它很可能会在与应用程序的其他部分交互时引起头痛。但这不是我能决定的。

【讨论】:

  • 请不要发布多个答案,而是编辑一个并添加第二个的内容
  • 猜你是对的。原因是,该操作有两个问题:“我可以在这里做些什么来正确加载它并关闭并重新打开”,另一个是堆栈跟踪显示的错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多