javaFx:stage如何正确关闭?stage.close()后发生了什么?
前言:stage.close()官方文档的说明其作用相当于stage.hide(),方法执行后stage并没有被正确关闭,如何才能正确关闭stage呢?下面建立一测试项目来探索一番。
一、建立项目msd,内有文件:
(一)、Msd.java 应用程序入口类;
(二)、javaFx控制器类 FXMLDocumentController.java
(三)、FXMLDocument.fxml、HtmlEdit.fxml 是视图文件。
FXMLDocument.fxml定义了一个按钮,HtmlEdit.fxml定义了一个html编辑器。当程序启动时装载FXMLDocument.fxml,点击其中的按钮实现页面跳转到HtmlEdit.fxml并进入全屏模式,按下ctrl+k退出全屏。再次点击按钮一次循环。
二、JavaFx控制器类 FXMLDocumentController.java实现事件消息的监听控制。
(一)、该控制器类实现了(implements)Initializable接口方法:public void initialize(URL url, ResourceBundle rb) {},声明并实例化
private final Stage stageTwo = new Stage();作为全局变量,
(二)、定义了事件消息处理器方法: private void handleButtonAction(ActionEvent event){},并调用loadFXML("HtmlEdit.fxml", stageTwo);实现页面的切换;
(三)、loadFXML()方法:装载第二个页面HtmlEdit.fxml并进入全屏模式,附上代码解析如下:
public AnchorPane loadFXML(String fxmlFileName, Stage stage) {
//工具方法:载入任意的FXML文件,并切换
if (fxmlFileName == null || fxmlFileName.isEmpty() || !fxmlFileName.endsWith(".fxml")) {
return null;
}
//单例模式:只能装载1次,直到关闭。
if (stage != null) {
if (stage.getScene() != null) {
if (stage.getScene().getUserData().toString().equals("showing")) {
if (!stage.isShowing()) {
//下面这条语句的作用是设置全屏模式退出的组合键
stage.setFullScreenExitKeyCombination(KeyCombination.valueOf("Ctrl+K"));
//下面这条语句的作用是设置全屏模式进入后在屏幕的提示
stage.setFullScreenExitHint("进入了全屏模式....");
//下面这条语句的作用是使stage进入全屏模式
stage.setFullScreen(true);
stage.show();
}
return (AnchorPane) stage.getScene().getRoot();
}
}
}
AnchorPane pane = null;
try {
pane = (AnchorPane) FXMLLoader.load(getClass().getResource(fxmlFileName));
} catch (IOException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
if (pane != null && stage != null) {
Scene scene = new Scene(pane);
scene.setUserData("showing");
stage.setScene(scene);
stage.show();
//监听舞台关闭事件:
stage.setOnCloseRequest((WindowEvent event) -> {
System.out.println("stage is hidded and then closed!");
stage.close();//注意!相当于stage.hide(),舞台并没有销毁。
//npe: System.out.println(stage.getUserData().toString());
});
//监听舞台隐藏事件(关闭按钮被点击默认是隐藏效果,同上)
stage.setOnHidden((WindowEvent event) -> {
System.out.println("stage hidded! ");
//npe: 因为onCloseRequest优先被执行了,stage.close() stage.getUserData().toString()
});
//监听舞台正在被隐藏,尚未隐藏完毕事件。优先于setOnHidded()
stage.setOnHiding((WindowEvent event) -> {
System.out.println("stage is hidding....");
});
//监听舞台正准备显示出来事件
stage.setOnShowing((WindowEvent event) -> {
System.out.println("stage is showing...");
});
//监听舞台显示完毕事件
stage.setOnShown((WindowEvent event) -> {
System.out.println("stage is showed now!");
});
stage.setFullScreenExitKeyCombination(KeyCombination.valueOf("Ctrl+K"));
stage.setFullScreenExitHint("进入了....");
stage.setFullScreen(true);
}
return pane;
}
三、问题
当stage进入全屏模式,按下ctrl+k退出后,“关闭”stage,再次点击按钮程序工作“正常”,但仔细研究就会发现,当我们将调用stage.close()将stage关闭后:
(一)、再次引用stage将会抛出NPE异常,说明stage已经被置为null引用。
stage.close();//注意!相当于stage.hide(),舞台并没有销毁。
//npe: System.out.println(stage.getUserData().toString());
(二)、再次点击按钮,却发现stage还在,以下代码被执行:
//单例模式:只能装载1次,直到关闭。
if (stage != null) { //注意这里!stage在第一次被close()后,再引用会抛出NPE;但是第二次点击按钮它却不是null
if (stage.getScene() != null) {
if (stage.getScene().getUserData().toString().equals("showing")) {
if (!stage.isShowing()) {
stage.setFullScreenExitKeyCombination(KeyCombination.valueOf("Ctrl+K"));
stage.setFullScreenExitHint("进入了....");
stage.setFullScreen(true);
stage.show();
}
return (AnchorPane) stage.getScene().getRoot();
}
}
}
而stage只是被实例化了一次,作为全局变量使用。当stage.close()调用后,stage并没有被真的销毁,照说应该会被垃圾回收器回收,然而结果还是没有被回收内存没有释放,对stage的引用却处于既是null又是非null的诡异状态中。