简体   繁体   English

JavaFX:舞台关闭处理程序

[英]JavaFX: Stage close handler

I want to save a file before closing my JavaFX application.我想在关闭 JavaFX 应用程序之前保存一个文件。

This is how I'm setting up the handler in Main::start :这就是我在Main::start设置处理程序的方式:

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

And the controller calling Stage::close when a button is pressed:当按下按钮时控制器调用Stage::close

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

If I close the window clicking the red X on the window border (the normal way) then I get the output message " Stage is closing ", which is the desired behavior.如果我单击窗口边框上的红色 X 关闭窗口(正常方式),则会收到输出消息“ Stage is closing ”,这是所需的行为。

However, when calling Controller::exitApplication the application closes without invoking the handler (there's no output).但是,当调用Controller::exitApplication ,应用程序关闭而不调用处理程序(没有输出)。

How can I make the controller use the handler I've added to primaryStage ?如何让控制器使用我添加到primaryStage的处理程序?

If you have a look at the life-cycle of the Application class:如果您查看Application类的生命周期

The JavaFX runtime does the following, in order, whenever an application is launched:每当启动应用程序时,JavaFX 运行时都会按顺序执行以下操作:

  1. Constructs an instance of the specified Application class构造指定的 Application 类的实例
  2. Calls the init() method调用init()方法
  3. Calls the start(javafx.stage.Stage) method调用start(javafx.stage.Stage)方法
  4. Waits for the application to finish, which happens when either of the following occur:等待应用程序完成,这在以下任一情况发生时发生:
    • the application calls Platform.exit()应用程序调用Platform.exit()
    • the last window has been closed and the implicitExit attribute on Platform is true最后一个窗口已关闭,并且Platform上的implicitExit属性为true
  5. Calls the stop() method调用stop()方法

This means you can call Platform.exit() on your controller:这意味着您可以在控制器上调用Platform.exit()

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

as long as you override the stop() method on the main class to save the file.只要你覆盖主类上的stop()方法来保存文件。

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

As you can see, by using stop() you don't need to listen to close requests to save the file anymore (though you can do it if you want to prevent window closing).如您所见,通过使用stop()您不再需要监听关闭请求来保存文件(尽管如果您想阻止窗口关闭,您可以这样做)。

Suppose you want to ask the user if he want to exit the application without saving the work.假设您想询问用户是否要退出应用程序而不保存工作。 If the user choose no, you cannot avoid the application to close within the stop method.如果用户选择否,则无法避免应用程序在 stop 方法中关闭。 In this case you should add an EventFilter to your window for an WINDOW_CLOSE_REQUEST event.在这种情况下,您应该为 WINDOW_CLOSE_REQUEST 事件向窗口添加一个 EventFilter

In your start method add this code to detect the event:在您的 start 方法中添加此代码以检测事件:

(Note that calling Platform.exit(); doesn't fire the WindowEvent.WINDOW_CLOSE_REQUEST event , see below to know how to fire the event manually from a custom button) (请注意,调用 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);

Then add your custom logic.然后添加您的自定义逻辑。 In my example i use an Alert popup to ask the user if he/she want to close the application without saving.在我的示例中,我使用警报弹出窗口询问用户他/她是否要关闭应用程序而不保存。

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();
            }
        }
    }

The event.consume() method prevents the application from closing . event.consume()方法防止应用程序关闭 Obviously you should add at least a button that permit the user to close the application to avoid the force close application by the user, that in some cases can corrupt data.显然,您应该至少添加一个允许用户关闭应用程序的按钮,以避免用户强制关闭应用程序,这在某些情况下可能会损坏数据。

Lastly, if you have to fire the event from a custom close button, you can use this :最后,如果您必须从自定义关闭按钮触发事件,您可以使用:

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

window.fireEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSE_REQUEST));

Ahh this is a known bug in JavaFX where the Stage will not close if a modal dialog is present at the time of closing.啊,这是 JavaFX 中的一个已知错误,如果在关闭时出现模式对话框,舞台将不会关闭。 I will link you to the bug report which I just saw today.我会将您链接到我今天刚刚看到的错误报告。 I think it is fixed in the latest release.认为它已在最新版本中修复。

Here you go:干得好:

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

resolved in 8.4 it says.它说在 8.4 中解决了。 I think this what you are describing.我想这就是你所描述的。

public Stage getParentStage() {
    return (Stage) getFxmlNode().getScene().getWindow();
}

btnCancel.setOnAction(e -> {
    getParentStage().close();
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM