web-dev-qa-db-fra.com

JavaFX: gestionnaire de scène

Je souhaite enregistrer un fichier avant de fermer mon application JavaFX.

Voici comment je configure le gestionnaire dans Main::start:

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

Et le contrôleur appelant Stage::close lorsqu'un bouton est enfoncé:

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

Si je ferme la fenêtre en cliquant sur le X rouge sur le bord de la fenêtre (normalement), le message de sortie "Stage is closing ", qui est le comportement souhaité.

Cependant, lorsque vous appelez Controller::exitApplication l'application se ferme sans appeler le gestionnaire (il n'y a pas de sortie).

Comment faire en sorte que le contrôleur utilise le gestionnaire que j'ai ajouté à primaryStage?

43
Heine Frade

Si vous regardez le cycle de vie de la classe Application:

Le runtime JavaFX effectue les opérations suivantes, dans l'ordre, chaque fois qu'une application est lancée:

  1. Construit une instance de la classe d'application spécifiée
  2. Appelle la méthode init()
  3. Appelle la méthode start(javafx.stage.Stage)
  4. Attend la fin de l'application, ce qui se produit lorsque l'une des situations suivantes se produit:
    • l'application appelle Platform.exit()
    • la dernière fenêtre a été fermée et l'attribut implicitExit de Platform est true
  5. Appelle la méthode stop()

Cela signifie que vous pouvez appeler Platform.exit() sur votre contrôleur:

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

tant que vous remplacez la méthode stop() sur la classe principale pour enregistrer le fichier.

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

Comme vous pouvez le constater, en utilisant stop(), vous n'avez plus besoin d'écouter les demandes de fermeture pour enregistrer le fichier (vous pouvez le faire si vous souhaitez empêcher la fermeture de la fenêtre).

82
José Pereda

Supposons que vous souhaitiez demander à l'utilisateur s'il souhaite quitter l'application sans enregistrer le travail. Si l'utilisateur choisit non, vous ne pouvez pas empêcher l'application de se fermer dans la méthode d'arrêt. Dans ce cas, vous devriez ajouter un EventFilter à votre fenêtre pour un événement WINDOW_CLOSE_REQUEST.

Dans votre méthode de démarrage, ajoutez ce code pour détecter l'événement:

(Notez que l'appel de Platform.exit (); ne déclenche pas l'événement WindowEvent.WINDOW_CLOSE_REQUEST , voir ci-dessous pour savoir comment. déclencher l'événement manuellement à partir d'un bouton personnalisé)

// *** 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);

Ajoutez ensuite votre logique personnalisée. Dans mon exemple, j'utilise une alerte contextuelle pour demander à l'utilisateur s'il souhaite fermer l'application sans enregistrer.

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

La méthode event.consume()empêche la fermeture de l'application. Évidemment, vous devez ajouter au moins un bouton permettant à l'utilisateur de fermer l'application pour éviter l'application forcée de fermer par l'utilisateur, ce qui peut dans certains cas altérer les données.

Enfin, si vous devez déclencher l'événement à partir d'un bouton de fermeture personnalisé, vous pouvez utiliser ceci:

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

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

Ahh c'est un bogue connu dans JavaFX où la scène ne se fermera pas si un dialogue modal est présent au moment de la fermeture. Je vous relierai au rapport de bogue que je viens de voir aujourd'hui. Je pense il est corrigé dans la dernière version.

Voici:

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

résolu en 8.4 il dit. Je pense que ce que vous décrivez.

1
user9659191