web-dev-qa-db-ja.com

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

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

((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()に追加します。最終的にステージを取得するリスナー処理。そして、すべては、イベントをステージングするために必要なリスナーを設定することで終わります。リスナーが多すぎると思います。それが私の問題を解決する唯一の方法ですか?

82
Chechulin

getController()を介して初期化した後、FXMLLoaderからコントローラーのインスタンスを取得できますが、静的メソッドを使用する代わりに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
109
Sebastian

必要なのは、AnchorPaneにIDを与えるだけで、そこからStageを取得できます。

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

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

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

92
Robert Martin

私はそれがあなたが望む答えではないことを知っていますが、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);
...
25
Utku Özdemir

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

  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. これで、コントローラーのステージにアクセスできます

10
Sandeep Kumar

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

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

希望、これはあなたのために働く!!

0
Ankit RajDeo