我想从我的控制器类中处理阶段事件(即隐藏)。所以我所要做的就是像这样添加一个监听器:
((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
添加一个监听器,当它不为空时,我得到场景,添加它是 windowProperty
我的监听器处理,我最终检索到舞台。这一切都以将所需的听众设置为舞台事件而告终。
我觉得听众太多了
这是解决我问题的唯一方法吗?
你可以通过
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
你只需要给
AnchorPane
一个ID,然后你就可以从中得到Stage
。
@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();
在这里,您可以添加您需要的
Listener
。
编辑:正如下面EarthMind所述,它不一定是
AnchorPane
元素;它可以是您定义的任何元素。
我知道这不是你想要的答案,但 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);
...
在控制器中获取舞台对象的最简单方法是:
在自己创建的控制器类中添加一个额外的方法(这将是一个设置方法来设置控制器类中的舞台),
private Stage myStage;
public void setStage(Stage stage) {
myStage = stage;
}
在启动方法中获取控制器并设置阶段
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
Parent parent = loader.load(); // Note the loader must be loaded before you can access the controller.
OwnController controller = loader.getController();
controller.setStage(this.stage);
现在您可以在控制器中访问舞台
Platform.runLater 用于阻止执行直到初始化完成。在这种情况下,我想在每次调整窗口宽度时刷新列表视图。
Platform.runLater(() -> {
((Stage) listView.getScene().getWindow()).widthProperty().addListener((obs, oldVal, newVal) -> {
listView.refresh();
});
});
你的情况
Platform.runLater(()->{
((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
});
分配 fx:id 或声明任何节点的变量:anchorpane、button 等。然后向其添加事件处理程序,并在该事件处理程序中插入下面给定的代码:
Stage stage = (Stage)((Node)((EventObject) eventVariable).getSource()).getScene().getWindow();
希望,这对你有用!!
可以用
node.getScene
获取,如果不从Platform.runLater
调用,结果是空值。
空值示例:
node.getScene();
没有空值的例子:
Platform.runLater(() -> {
node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
//your event
});
});