【问题标题】:JavaFX: Stage close handlerJavaFX:阶段关闭处理程序
【发布时间】:2014-12-24 12:29:54
【问题描述】:

我想在关闭我的 JavaFX 应用程序之前保存一个文件。

这就是我在Main::start 中设置处理程序的方式:

primaryStage.setOnCloseRequest(event -> {
    System.out.println("Stage is closing");
    // Save file
});

当按下按钮时控制器调用Stage::close

@FXML
public void exitApplication(ActionEvent event) {
    ((Stage)rootPane.getScene().getWindow()).close();
}

如果我单击窗口边框上的红色 X 关闭窗口(正常方式),则会收到输出消息“Stage is closing”,这是所需的行为。

但是,当调用 Controller::exitApplication 时,应用程序会在不调用处理程序的情况下关闭(没有输出)。

如何让控制器使用我添加到primaryStage 的处理程序?

【问题讨论】:

    标签: java javafx handler javafx-8


    【解决方案1】:

    如果您查看 Application 类的 life-cycle

    JavaFX 运行时按顺序执行以下操作,只要 应用程序启动:

    1. 构造指定应用程序类的实例
    2. 调用init()方法
    3. 调用start(javafx.stage.Stage)方法
    4. 等待应用程序完成,当发生以下任一情况时会发生这种情况:
      • 应用程序调用Platform.exit()
      • 最后一个窗口已关闭,Platform 上的implicitExit 属性为true
    5. 调用stop()方法

    这意味着您可以在控制器上调用Platform.exit()

    @FXML
    public void exitApplication(ActionEvent event) {
       Platform.exit();
    }
    

    只要重写主类上的stop() 方法即可保存文件。

    @Override
    public void stop(){
        System.out.println("Stage is closing");
        // Save file
    }
    

    如您所见,通过使用stop(),您不再需要收听关闭请求来保存文件(但如果您想阻止窗口关闭,您可以这样做)。

    【讨论】:

      【解决方案2】:

      假设您想询问用户是否要退出应用程序而不保存工作。如果用户选择 no,则无法避免应用程序在 stop 方法内关闭。在这种情况下,您应该为 WINDOW_CLOSE_REQUEST 事件将 EventFilter 添加到您的窗口

      在你的 start 方法中添加这个代码来检测事件:

      (请注意,调用 Platform.exit(); 不会触发 WindowEvent.WINDOW_CLOSE_REQUEST 事件,请参阅下文了解如何从自定义手动触发事件按钮)

      // *** Only for Java >= 8 ****
      // ==== This code detects when an user want to close the application either with
      // ==== the default OS close button or with a custom close button ====
      
      primaryStage.getScene().getWindow().addEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, this::closeWindowEvent);
      

      然后添加您的自定义逻辑。在我的示例中,我使用警报弹出窗口询问用户他/她是否要关闭应用程序而不保存。

      private void closeWindowEvent(WindowEvent event) {
              System.out.println("Window close request ...");
      
              if(storageModel.dataSetChanged()) {  // if the dataset has changed, alert the user with a popup
                  Alert alert = new Alert(Alert.AlertType.INFORMATION);
                  alert.getButtonTypes().remove(ButtonType.OK);
                  alert.getButtonTypes().add(ButtonType.CANCEL);
                  alert.getButtonTypes().add(ButtonType.YES);
                  alert.setTitle("Quit application");
                  alert.setContentText(String.format("Close without saving?"));
                  alert.initOwner(primaryStage.getOwner());
                  Optional<ButtonType> res = alert.showAndWait();
      
                  if(res.isPresent()) {
                      if(res.get().equals(ButtonType.CANCEL))
                          event.consume();
                  }
              }
          }
      

      event.consume() 方法阻止应用程序关闭。显然,您应该至少添加一个允许用户关闭应用程序的按钮,以避免用户强制关闭应用程序,这在某些情况下会损坏数据。

      最后,如果你必须从自定义关闭按钮触发事件,你可以使用这个:

      Window window = Main.getPrimaryStage()  // Get the primary stage from your Application class
                      .getScene()
                      .getWindow();
      
      window.fireEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSE_REQUEST));
      

      【讨论】:

      • 对我的用例非常有用,我有一个简单的对话框并且没有继承 Application 类。
      【解决方案3】:

      啊,这是 JavaFX 中的一个已知错误,如果在关闭时存在模式对话框,舞台将不会关闭。我会将您链接到我今天刚刚看到的错误报告。我认为它已在最新版本中修复。

      给你:

      https://bugs.openjdk.java.net/browse/JDK-8093147?jql=text%20~%20%22javafx%20re-entrant%22

      它说在 8.4 中解决。我想这就是你所描述的。

      【讨论】:

        【解决方案4】:
        public Stage getParentStage() {
            return (Stage) getFxmlNode().getScene().getWindow();
        }
        
        btnCancel.setOnAction(e -> {
            getParentStage().close();
        });
        

        【讨论】:

        • 很好,您试图提供帮助 - 但这并不能解决 OP 的问题:没有按钮 ;)
        猜你喜欢
        • 2023-04-01
        • 2012-04-30
        • 2019-01-09
        • 1970-01-01
        • 2014-04-21
        • 2014-04-07
        • 2014-07-10
        • 2017-11-10
        相关资源
        最近更新 更多