98

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

((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() 我的!该死的!我最终检索到的监听器处理阶段。这一切都以设置所需的侦听器为舞台事件而告终。我觉得听众太多了。这是解决我的问题的唯一方法吗?

4

7 回答 7

124

FXMLLoader您可以在初始化之后通过获取控制器的实例getController(),但您需要实例化 anFXMLLoader而不是使用静态方法。

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
于 2012-11-06T08:18:21.607 回答
119

您只需要提供AnchorPane一个 ID,然后您就可以从中获取Stage

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

从这里,您可以添加Listener您需要的内容。

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

于 2015-07-28T20:48:07.853 回答
33

我知道这不是您想要的答案,但 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!");
                    }
                });
            }
        });
    }
});

2-您在创建的地方做您需要做的事情Stage(这不是您想要的):

Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
            if (c) {
                System.out.println("I am maximized!");
            }
        });
stage.setScene(someScene);
...
于 2015-06-18T08:19:18.587 回答
17

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

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

    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. 现在您可以访问控制器中的舞台

于 2014-10-15T04:17:31.417 回答
2

Platform.runLater 用于阻止执行,直到初始化完成。在这种情况下,我想在每次调整窗口宽度时刷新列表视图。

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

在你的情况下

Platform.runLater(()->{
    ((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
});
于 2020-01-23T14:31:58.750 回答
1

为任何节点分配fx:id或声明变量:锚窗格、按钮等。然后向其添加事件处理程序,并在该事件处理程序中插入以下给定代码:

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

希望,这对你有用!

于 2018-08-11T10:13:57.187 回答
-2

你可以得到 with node.getScene,如果你不调用 from Platform.runLater,结果是一个空值。

空值示例:

node.getScene();

示例没有空值:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
于 2017-08-12T16:48:36.547 回答