103 votes

JavaFX : Comment récupérer l'étape du contrôleur pendant l'initialisation ?

Je veux gérer les événements de scène (c'est-à-dire les cacher) à partir de ma classe de contrôleur. Tout ce que j'ai à faire, c'est d'ajouter un écouteur via la fonction

((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);

mais le problème est que l'initialisation commence juste après

Parent root = FXMLLoader.load(getClass().getResource("MyGui.fxml"));

et avant

Scene scene = new Scene(root);
stage.setScene(scene);

Ainsi, .getScene() renvoie null.

La seule solution que j'ai trouvée par moi-même est d'ajouter un listener à myPane.sceneProperty(), et lorsqu'il devient non nul, je récupère scene, ajoute à son .windowProperty() ma !putain ! de manipulation de listener que je récupère finalement stage. Et tout cela se termine par la mise en place des listeners souhaités pour les événements de la scène. Je pense qu'il y a trop de listeners. Est-ce le seul moyen de résoudre mon problème ?

129voto

Sebastian Points 2813

Vous pouvez obtenir l'instance du contrôleur à partir de l'élément FXMLLoader après l'initialisation via getController() mais vous devez instancier un FXMLLoader au lieu d'utiliser les méthodes statiques à ce moment-là.

Je passais la scène après avoir appelé load() directement au contrôleur par la suite :

FXMLLoader loader = new FXMLLoader(getClass().getResource("MyGui.fxml"));
Parent root = (Parent)loader.load();
MyController controller = (MyController)loader.getController();
controller.setStageAndSetupListeners(stage); // or what you want to do

1 votes

Vous pensez qu'il vous manque un plâtre ? Parent Root = (Parent)loader.load() ;

17 votes

Est-ce vraiment la meilleure façon de procéder ? Le cadre JavaFX ne fournit rien pour archiver ceci ?

1 votes

Pour une raison quelconque, cela ne fonctionne pas si vous utilisez le constructeur sans paramètres et que vous transmettez l'URL à l'utilisateur. load() méthode. (De même, la javadoc sur [getController](http://docs.oracle.com/javafx/2/api/javafx/fxml/FXMLLoader.html#getController()) on dirait qu'il devrait être sur setController .)

125voto

Robert Martin Points 1464

Il vous suffit de donner à l AnchorPane un ID, et ensuite vous pouvez obtenir le Stage de cela.

@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();

À partir de là, vous pouvez ajouter le Listener dont vous avez besoin.

Edit : Comme indiqué par EarthMind ci-dessous, il n'est pas nécessaire que ce soit la AnchorPane Il peut s'agir de n'importe quel élément que vous avez défini.

33voto

Utku Özdemir Points 575

Je sais que ce n'est pas la réponse que vous souhaitez, mais, selon moi, les solutions proposées ne sont pas bonnes (et votre propre solution l'est). Pourquoi ? Parce qu'elles dépendent de l'état de l'application. Dans JavaFX, un contrôle, une scène et une scène ne dépendent pas les uns des autres. Cela signifie qu'un contrôle peut vivre sans être ajouté à une scène et qu'une scène peut exister sans être attachée à un stage. Et puis, à un instant t1, un contrôle peut être attaché à une scène et à un instant t2, cette scène peut être ajoutée à un stage (et cela explique pourquoi ils sont des propriétés observables les uns des autres).

Ainsi, l'approche qui suggère de récupérer la référence du contrôleur et d'invoquer une méthode, en lui passant le stage, ajoute un état à votre application. Cela signifie que vous devez invoquer cette méthode au bon moment, juste après la création de la scène. En d'autres termes, vous devez suivre un ordre maintenant : 1- Créer la scène 2- Passer cette étape créée au contrôleur via une méthode.

Vous ne pouvez (ou ne devez) pas modifier cet ordre dans cette approche. Vous avez donc perdu l'apatridie. Et dans les logiciels, en général, l'état est un mal. Idéalement, les méthodes ne devraient pas nécessiter d'ordre d'appel.

Quelle est donc la bonne solution ? Il existe deux possibilités :

1- Votre approche, dans le contrôleur écouter les propriétés pour obtenir la scène. Je pense que c'est la bonne approche. Comme ceci :

pane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {
    if (oldScene == null && newScene != null) {
        // scene is set for the first time. Now its the time to listen stage changes.
        newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> {
            if (oldWindow == null && newWindow != null) {
                // stage is set. now is the right time to do whatever we need to the stage in the controller.
                ((Stage) newWindow).maximizedProperty().addListener((a, b, c) -> {
                    if (c) {
                        System.out.println("I am maximized!");
                    }
                });
            }
        });
    }
});

2- Vous faites ce que vous devez faire là où vous créez la Stage (et ce n'est pas ce que vous voulez) :

Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
            if (c) {
                System.out.println("I am maximized!");
            }
        });
stage.setScene(someScene);
...

18voto

Sandeep Kumar Points 1

La façon la plus simple d'obtenir l'objet stage dans le contrôleur est :

  1. Ajoutez une méthode supplémentaire dans votre propre classe de contrôleur créée comme (ce sera une méthode setter pour définir la scène dans la classe de contrôleur),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
  2. Obtenir le contrôleur dans la méthode de démarrage et la phase de réglage

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
  3. Maintenant vous pouvez accéder à la scène dans le contrôleur

0 votes

C'était le gagnant pour moi. La réponse de Robert Martin a d'abord fonctionné, mais elle a ensuite déclenché une erreur que j'ai résolue en obtenant stage comme ça.

3voto

AdamOutler Points 360

Platform.runLater fonctionne pour empêcher l'exécution jusqu'à ce que l'initialisation soit terminée. Dans ce cas, je veux rafraîchir une vue de liste chaque fois que je redimensionne la largeur de la fenêtre.

Platform.runLater(() -> {
    ((Stage) listView.getScene().getWindow()).widthProperty().addListener((obs, oldVal, newVal) -> {
        listView.refresh();
    });
});

dans votre cas

Platform.runLater(()->{
    ((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X