JavaFX:如何在初始化期间从控制器获取阶段?

Che*_*lin 83 javafx initialization stage javafx-2 javafx-8

我想从我的控制器类处理阶段事件(即隐藏).所以我要做的就是通过添加一个监听器

((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
Run Code Online (Sandbox Code Playgroud)

但问题是初始化刚刚开始

Parent root = FXMLLoader.load(getClass().getResource("MyGui.fxml"));
Run Code Online (Sandbox Code Playgroud)

之前

Scene scene = new Scene(root);
stage.setScene(scene);
Run Code Online (Sandbox Code Playgroud)

因此.getScene()返回null.

我自己找到的唯一解决方法是向myPane.sceneProperty()添加一个监听器,当它变为非null时,我得到场景,添加到它的.windowProperty()我的!该死!我最终检索阶段的监听器处理.这一切都以设置所需的听众来举办舞台活动而告终.我认为听众太多了.这是解决我问题的唯一方法吗?

Seb*_*ian 110

您可以从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
Run Code Online (Sandbox Code Playgroud)

  • 这真的是最好的方法吗?JavaFX框架没有提供存档的东西吗? (11认同)
  • @Bombe这是因为带有URL参数的load()方法是一个静态方法,它不会在您调用它的实例上设置任何内容. (4认同)

Rob*_*tin 98

你只需要提供AnchorPane一个ID,然后就可以得到Stage它.

@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();
Run Code Online (Sandbox Code Playgroud)

从这里,您可以添加Listener所需的内容.

编辑:如下面的EarthMind所述,它不一定是AnchorPane元素; 它可以是您定义的任何元素.

  • `getScene()`仍然返回`null`. (25认同)
  • 在初始化完成之前(例如在控制器初始化方法中),这将不起作用,因为getScene()返回null.原始海报暗示这是他的情况.在这种情况下,下面的UtkuÖzdemir给出的替代方案1更好.如果你不需要这个阶段那么一个监听器就足够了,我在initialize()中使用它来创建一个ImageView伸展:`bgimage.sceneProperty().addListener((observableScene,oldScene,newScene) - > {if(oldScene = = null && newScene!= null){bgimage.fitWidthProperty().bind(newScene.widthProperty()); ...}});` (11认同)
  • 请注意,`element`可以是该窗口中具有`fx:id`的任何元素:`Stage stage =(Stage)element.getScene().getWindow();`.例如,如果您的窗口中只有一个带有`fx:id`的`Button`,请使用它来获取舞台. (6认同)
  • 这是答案,整洁! (3认同)
  • 简短而甜美。谢谢 (2认同)

Utk*_*mir 27

我知道这不是你想要的答案,但IMO提出的解决方案并不好(而且你自己的方式是).为什么?因为它们取决于应用程序状态.在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!");
                    }
                });
            }
        });
    }
});
Run Code Online (Sandbox Code Playgroud)

2-你在你创建的地方做你需要做的事Stage(那不是你想要的):

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


小智 10

在控制器中获取舞台对象的最简单方法是:

  1. 在自己创建的控制器类中添加一个额外的方法(它将是一个setter方法来设置控制器类中的阶段),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在启动方法和设置阶段获取控制器

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在您可以访问控制器中的舞台