JavaFX: Comment obtenir l'étape du contrôleur lors de l'initialisation?

89

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

((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 de contournement que j'ai trouvée par moi-même est d'ajouter un écouteur à myPane.sceneProperty (), et quand il devient non nul, j'obtiens une scène, ajoutez-y .windowProperty () my! Goddamn! la manipulation de l'auditeur que je récupère enfin stage. Et tout se termine par la mise en scène des auditeurs souhaités. Je pense qu'il y a trop d'auditeurs. Est-ce la seule façon de résoudre mon problème?

Chechulin
la source

Réponses:

116

Vous pouvez obtenir l'instance du contrôleur FXMLLoaderaprès l'initialisation via getController(), mais vous devez instancier unFXMLLoader au lieu d'utiliser les méthodes statiques.

Je passerais l'étape après avoir appelé load()directement le 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
Zhujik
la source
1
Vous pensez qu'il vous manque un casting? Parent root = (Parent) loader.load ();
Paul Eden
12
Est-ce vraiment la meilleure façon de procéder? Il n'y a rien fourni par le framework JavaFX pour archiver cela?
Hannes
1
Pour une raison quelconque, cela ne fonctionne pas si vous utilisez le constructeur sans paramètres et transmettez l'URL à la load()méthode. (De plus, le javadoc sur getControllersonne comme s'il devrait être setController
activé
4
@Bombe c'est parce que la méthode load () avec le paramètre URL est une méthode statique qui ne définit rien sur l'instance sur laquelle vous l'appelez.
zhujik le
@Sebastian, aïe, en effet. Ma faute.
Bombe le
109

Tout ce dont vous avez besoin est de donner AnchorPaneun identifiant, puis vous pouvez l'obtenir Stage.

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

De là, vous pouvez ajouter le Listenerdont vous avez besoin.

Edit: Comme indiqué par EarthMind ci-dessous, il n'est pas nécessaire que ce soit l' AnchorPaneélément; cela peut être n'importe quel élément que vous avez défini.

Robert Martin
la source
2
Court et doux. Merci
Sedrick
7
Notez que elementpeut être un élément avec un fx:iddans cette fenêtre: Stage stage = (Stage) element.getScene().getWindow();. Par exemple, si vous n'avez qu'un Buttonavec un fx:iddans vos fenêtres, utilisez-le pour accéder à la scène.
EarthMind
28
getScene()revient toujours null.
m0skit0
3
C'est la réponse, soignée!
destan
12
Avant la fin de l'initialisation (par exemple dans la méthode d'initialisation du contrôleur), cela ne fonctionnera pas car getScene () renvoie null. L'affiche originale laisse entendre que c'est sa situation. Dans ce cas, la variante 1 donnée par Utku Özdemir ci-dessous est meilleure. Si vous n'avez pas besoin de la scène, un auditeur suffit, j'utilise ceci dans initialize () pour créer un étirement ImageView:bgimage.sceneProperty().addListener((observableScene, oldScene, newScene) -> { if (oldScene == null && newScene != null) { bgimage.fitWidthProperty().bind(newScene.widthProperty()); ... } });
Georgie
32

Je sais que ce n'est pas la réponse que vous voulez, mais à l'OMI, les solutions proposées ne sont pas bonnes (et à votre façon). Pourquoi? Parce qu'ils dépendent de l'état de l'application. Dans JavaFX, un contrôle, une scène et une étape 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 à une scène. Et puis, à un instant t1, le contrôle peut s'attacher à une scène et à l'instant t2, cette scène peut être ajoutée à une étape (et cela explique pourquoi ce sont des propriétés observables l'une de l'autre).

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

Vous ne pouvez pas (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 mauvais. Idéalement, les méthodes ne devraient nécessiter aucun ordre d'appel.

Alors, quelle est la bonne solution? Il existe deux alternatives:

1- Votre approche, dans les propriétés d'écoute du contrôleur pour obtenir la scène. Je pense que c'est la bonne approche. Comme ça:

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 le 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);
...
Utku Özdemir
la source
1
Il s'agit de la référence JavaFX la plus courte de la planète !! Merci Utku!
Aram Paronikyan
Intéressante façon de voir les choses. Thanks :)
Utku Özdemir
J'essaie d'utiliser cette approche pour remplir un TableView et afficher un respect centré ProgressIndicator sur le TableView, mais il s'avère qu'à l'intérieur des événements, les valeurs de getX () et getY () ne sont pas les mêmes que si vous utilisez après toute l'initialisation processus fin (à l'intérieur d'un événement de clic dans un bouton) soit pour la scène et l'étape, même l'étape getX () et getY () retournent NaN, une idée?
leobelizquierdo
@leobelizquierdo, vous avez ce problème getY () en ce moment, avez-vous trouvé une solution?
JonasAnon
si j'ajoute paneà une scène qui est déjà attachée à une scène, cela ne fonctionnera pas, non? Ne devrait-il pas y avoir une vérification dans l' scenePropertyécouteur et ajouter seulement l' stagePropertyécouteur si l'étape est toujours nulle? Sinon, fais ce dont j'ai besoin tout de suite?
demain
14

Le moyen le plus simple d'obtenir un objet de scène dans le contrôleur est:

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

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
  2. Obtenez le contrôleur dans la méthode de démarrage et définissez la scène

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

Sandeep Kumar
la source
C'était le gagnant pour moi. La réponse de Robert Martin a fonctionné au début, mais a ensuite commencé à lancer une erreur que j'ai résolue en obtenant stagecomme ça.
Dammeul
1

Assignez fx: id ou déclarez une variable à / de n'importe quel nœud: anchorpane, bouton, etc. Ajoutez ensuite un gestionnaire d'événements et dans ce gestionnaire d'événements, insérez le code donné ci-dessous:

Stage stage = (Stage)((Node)((EventObject) eventVariable).getSource()).getScene().getWindow();

J'espère que cela fonctionne pour toi!!

Ankit RajDeo
la source
1

Platform.runLater empêche l'exécution tant que l'initialisation n'est pas terminée. Dans ce cas, je souhaite actualiser 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 ton cas

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

Vous pouvez obtenir avec node.getScene, si vous n'appelez pas dePlatform.runLater , le résultat est une valeur nulle.

exemple de valeur nulle:

node.getScene();

exemple aucune valeur nulle:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
fjqa86
la source