JavaFX:初期化中にコントローラーからステージを取得する方法は?


89

コントローラクラスからステージイベント(つまり、非表示)を処理したい。だから私がしなければならないすべては経由でリスナーを追加することです

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

しかし問題は初期化が直後に始まるということです

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

以前

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

したがって、.getScene()はnullを返します。

私が見つけた唯一の回避策は、myPane.sceneProperty()にリスナーを追加することです。それがnullにならない場合は、シーンを取得し、.windowProperty()に追加します。最終的にステージを取得するリスナー処理。そして、すべての目的は、イベントをステージングするために必要なリスナーを設定することです。リスナーが多すぎると思います。それが私の問題を解決する唯一の方法ですか?

回答:


116

FXMLLoader初期化後のからコントローラのインスタンスを取得できますが、その場合は静的メソッドを使用getController()するFXMLLoader代わりにをインスタンス化する必要があります。

load()その後、コントローラーを直接呼び出した後、ステージを渡します。

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
キャストが足りないと思いますか?親ルート=(Parent)loader.load();
ポールエデン

12
これは本当にこれを行う最良の方法ですか?これをアーカイブするためにJavaFXフレームワークによって提供されるものは何もありませんか?
Hannes

1
何らかの理由で、パラメーターなしでコンストラクターを使用し、load()メソッドへのURLを渡した場合、これは機能しません。(また、JavadocはオンになってgetControllerいるように聞こえsetControllerます。)
Bombe

4
@Bombeこれは、URLパラメーターを指定したload()メソッドが、呼び出しているインスタンスに何も設定しない静的メソッドであるためです。
zhujik 2015

@セバスチャン、そうだね。私の悪い。
ボンベ2015

109

必要なのはAnchorPaneIDを指定することだけで、それからを取得できますStage

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

ここから、必要なものを追加できListenerます。

編集:以下のEarthMindで述べられているように、AnchorPane要素である必要はありません。これは、定義した任意の要素にすることができます。


2
短くて甘い。ありがとう
セドリック

7
elementfx:id、そのウィンドウにがある任意の要素であることに注意してくださいStage stage = (Stage) element.getScene().getWindow();。たとえば、ウィンドウにのButtonあるのみがある場合は、それをfx:id使用してステージを取得します。
EarthMind 2016年

28
getScene()まだ戻りますnull
m0skit0 2016年

3
これが正解です。
destan

12
初期化が完了する前に(たとえば、コントローラの初期化メソッドで)、getScene()がnullを返すため、これは機能しません。元のポスターは、これが彼の状況であることを示唆しています。この場合、以下のUtkuÖzdemirが提供する代替1の方が適しています。あなたは1リスナーが十分ある段階を必要としない場合、私はImageViewのストレッチをするために、初期化()でこれを使用しますbgimage.sceneProperty().addListener((observableScene, oldScene, newScene) -> { if (oldScene == null && newScene != null) { bgimage.fitWidthProperty().bind(newScene.widthProperty()); ... } });
ジョージー

32

私はそれがあなたが望む答えではないことを知っていますが、IMO提案されたソリューションは良くありません(そしてあなた自身の方法はそうです)。どうして?アプリケーションの状態に依存するためです。JavaFXでは、コントロール、シーン、ステージは互いに依存しません。つまり、コントロールはシーンに追加せずにライブでき、シーンはステージに接続しなくても存在できます。次に、t1の時点でコントロールがシーンにアタッチされ、t2の時点でそのシーンをステージに追加できます(これにより、お互いの観測可能なプロパティがわかります)。

そのため、コントローラー参照を取得してメソッドを呼び出し、それにステージを渡すことを提案するアプローチは、アプリケーションに状態を追加します。つまり、ステージが作成された直後に、適切なタイミングでそのメソッドを呼び出す必要があります。つまり、今すぐ注文に従う必要があります。1-ステージを作成する2-この作成されたステージをメソッドを介してコントローラーに渡す。

このアプローチでは、この順序を変更することはできません(または変更しないでください)。だからあなたは無国籍を失った。そして、ソフトウェアでは、一般的に状態は悪です。理想的には、メソッドは呼び出し順序を必要としません。

それで、正しい解決策は何ですか?2つの選択肢があります。

1-あなたのアプローチ、ステージを取得するためのコントローラーのリスニングプロパティ。これは正しいアプローチだと思います。このような:

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-あなたはあなたがあなたが作成した場所であなたがする必要があることをしますStage(そしてそれはあなたが望むものではありません):

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

1
これは、世界で最も短いJavaFXリファレンスです!! ありがとう、Utku!
Aram Paronikyan 2015

それを見る興味深い方法。ありがとう:)
UtkuÖzdemir

このアプローチを使用してTableViewにデータを入力し、TableViewを中心としたProgressIndicatorを表示しようとしていますが、getX()およびgetY()のイベント値は、すべての初期化後に使用した場合と同じではないことがわかりますシーンとステージのいずれかのプロセス終了(ボタンのクリックイベント内)、さらにはgetX()とgetY()もNaNを返します。
leobelizquierdo

@leobelizquierdoは、現時点でgetY()の問題を抱えていますが、解決策を見つけましたか?
JonasAnon 2017年

paneすでにステージに取り付けられているシーンに追加するとうまくいきませんよね?scenePropertyリスナーにチェックがあるべきではなくstageProperty、ステージがまだnullの場合にのみリスナーを追加しますか?それ以外の場合は、必要なことをすぐに実行しますか?
明日

14

コントローラでステージオブジェクトを取得する最も簡単な方法は次のとおりです。

  1. 独自に作成したコントローラークラスに追加のメソッドを追加します(これは、コントローラークラスにステージを設定するセッターメソッドになります)。

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
  2. startメソッドでコントローラーを取得し、ステージを設定する

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
  3. これで、コントローラーのステージにアクセスできます


これは私にとって勝者でした。ロバートマーティンの答えは最初はうまくいきましたが、次のstageようにして解決したエラーをスローし始めました。
ダムミュール2018

1

fx:idを割り当てるか、任意のノード(/アンカーノード、ボタンなど)に変数を宣言します。次に、それにイベントハンドラーを追加し、そのイベントハンドラー内に以下のコードを挿入します。

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

これでうまくいきますように!!


1

Platform.runLaterは、初期化が完了するまで実行されないように機能します。この場合、ウィンドウの幅を変更するたびにリストビューを更新します。

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

あなたの場合

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

-3

あなたはで取得することができnode.getSceneますから呼び出さない場合は、Platform.runLaterます。、結果はnull値になります。

null値の例:

node.getScene();

null値なしの例:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.