FXML JavaFXで他のstageのcontrollerを参照する方法


javaFXで複数のStageを使用するアプリケーションを作っていたのですが、

A_Stage,A_.fxmlで使用するA_Controllerと
B_Stage,B_.fxmlで使用するB_Controllerがあったとして
B_ControllerからA_Controllerのインスタンスのメソッドにアクセスするやり方がわかりませんでした。

散々ググって似たようなタイトルのサイトを真似しても自分がやりたい事と違いうまくいかず、FXML壊したり、NullPointerExceptionに悩まされてきたのですが、やっとわかったので備忘録で書いています。

とにかくどうやってA_Controllerへのポインタ参照をB_Controllerに持って来るかが問題です。

思いついた間違い1:
A_ControllerからB_Stageを作るとき、コンストラクタをオーバーロードできるようにstageクラスをextends継承して引数で渡す。
→無理です。いやもしかしたら出来るかもしれないけど相当難しそう。

思いついた間違い2:
B_Controllerの中でA_.fxmlを引数にFXMLLoader.getController()を使ってA_Contorollerを呼び出してA_Controllerのメソッドにアクセスする。
→インテリセンスも働くし一見動きそうですがNullPointerExceptionが発生します。どうやら同じFXMLを二回ロードしたら同じクラスの別々のインスタンスを2つnewされるため、ポインタを得られるわけではないそうです。

思いついた間違い3:
Controllerクラスかインタフェースがあるだろうからその変数を用意してどうとかする。
→そんなクラスありません。よく考えたらA_ControllerだろうがB_Controllerだろうが何も継承していない自作クラスでした。fxmlの方から指定して関連づけているだけでしたね。

成功:
public static void main(String args[]){}があるMainクラスに
静的メンバとしてA_Controllerクラスの変数 a_controllerを宣言し、
void start(Stage primaryStage)の中で
a_controller = fxmlLoader.getController();する。

後は、呼び出したいB_Contorollerクラスの好きなところで、
A_Controller controller;
contoroller = Main.a_controller;
contoroller.使いたいA_Controllerのメソッドやメンバ変数。
とすれば使える。

:Main.java
public class Main extends Application {

    public static A_Controller; //これを使いたい場所からMain.A_Controllerで呼び出す。
    @Override
    public void start(Stage primaryStage) {

        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("TradeLogTableStage.fxml"));
            Scene scene = new Scene((BorderPane)fxmlLoader.load());
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
            A_Controller = fxmlLoader.getController();//これで静的メンバにポインタが格納される。
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
:B_Controller.java
/** Testボタンというボタンが押された時に発動するイベントとする*/
    @FXML protected void onTestButtonClick(ActionEvent evt){
        System.out.println("TestButton Clicked");
        A_Controller controller;
        controller = Main.A_Controller;//これで自由にA_Controllerにアクセスできる
        System.out.println(controller.toString());//後は普通にクラスメンバを使うだけ。

    }
A_Controller.java
/**これはなんでもいいけど仮にこれをB_Controllerで呼びたいとする*/
    @Override public String toString(){
        return "This is the Controller which is Controll fxml";
    }

結果 B_Stageのテストボタンを押したところ。

結局やってることは静的メンバを使用しているだけなので、JavaFXに限らずSwingでも同じように使えると思います。

ただこのままだとウィンドウハンドルになる重要な参照変数がどこからでも簡単に掴めるわけで、一人で開発するぶんにはいいですけど、他人数で開発する場合はgetter/setterとかでうまくカプセル化しないと危ないかもしれません。