JavaFX: обработчик закрытия этапа

Я хочу сохранить файл перед закрытием моего приложения 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?

3 ответов


если вы посмотрите на жизненный цикл на Application класс:

среда выполнения JavaFX делает следующее, в порядке, когда приложение запускается:

  1. создает экземпляр указанного класса приложения
  2. называет init() метод
  3. называет start(javafx.stage.Stage) метод
  4. ожидает завершения приложения, что происходит, когда происходит одно из следующих событий:
    • приложения вызовы Platform.exit()
    • последнее окно закрыто и implicitExit атрибут on Platform is 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() вы больше не нужно слушать запросы на закрытие, чтобы сохранить файл (хотя вы можете сделать это, если хотите предотвратить закрытие окна).


Предположим, вы хотите спросить пользователя, хочет ли он выйти из приложения без сохранения работы. Если пользователь выбирает нет, вы не можете избежать закрытия приложения в методе stop. В этом случае вы должны добавить EventFilter в окно для события WINDOW_CLOSE_REQUEST.

в методе start добавьте этот код для обнаружения события:

(обратите внимание, что призвание.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));

Ahh это известная ошибка в JavaFX, где этап не закроется, если модальный диалог присутствует во время закрытия. Я свяжу вас с отчетом об ошибке, который я только что видел сегодня. Я!--1-->думаю это исправлено в последней версии.

вот, пожалуйста:

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

разрешено в 8.4, говорит он. Я думаю, это то, что вы описываете.