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

问题描述 投票:85回答:7

我想处理控制器类中的舞台事件(即隐藏)。所以我要做的就是通过

添加一个侦听器
((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()中!该死!侦听器处理,这是我终于检索到的阶段。最后,设置所需的侦听器来安排事件。我认为听众太多。这是解决我的问题的唯一方法吗?

javafx initialization javafx-2 javafx-8 stage
7个回答
112
投票

您可以在通过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

99
投票

您所需要做的就是给AnchorPane一个ID,然后您可以从中获得Stage

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

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

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


28
投票

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

11
投票

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

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


0
投票

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

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

希望,这对您有用!


0
投票

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

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

-3
投票

您可以通过node.getScene获得,如果不从Platform.runLater进行调用,则结果为空值。

示例空值:

node.getScene();

示例,无空值:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
© www.soinside.com 2019 - 2024. All rights reserved.