FXML から FXML を参照する

JavaFX で一つのウィンドウに同じ要素(コンポーネント?)をいくつか貼り付けたいなーとかいう場合。例えば二画面ファイラーの左右ファイル窓とかですね。これらは左右で全く構造が同じです。または複数の画面から同じ要素を参照したい場合。いくら Scene Builder でぽとぺた便利だからって全く同じものを二つ作るのはメンテナンス的にも精神衛生的にもよろしくないですね。

というわけで共通要素だけ別の FXML に抜き出して、親 FXML から参照するとかできないのかなーと思って調べると Nested Controllers なんてのが見つかりました。

fx:include なるものを使って FXML から別の FXML を読み込む感じのようです*1。親コントローラーから @FXML を使って子のコントローラーや子の要素自身をインジェクションさせることもできます。

上記ページでは親コントローラーに子要素を Window としてインジェクションするようになってますが、ここは子の FXML のルート要素の型に合わせる感じになるようです。また、親コントローラーに子コントローラーをインジェクションする場合、 fx:include で指定した id + Controller というフィールド名にする必要があるようです。ここが違ってると子コントローラーのフィールドが null になります。

親の FXML がこんな感じ(だいぶはしょってます)だったとすると、

<AnchorPane id="AnchorPane" xmlns:fx="http://javafx.com/fxml" fx:controller="sample.MainWindowController">
    <children>
        <fx:include fx:id="child" source="childView.fxml"/>
    </children>
</AnchorPane>

親のコントローラーはこんな感じになります。

public class MainWindowController implements Initializable {

    @FXML
    private AnchorPane child;
    @FXML
    private ChildController childController;
    // 子要素の id 名 + Controller にする必要があるので、これはだめ
    // private ChildController childXXXController;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // 子コントローラーを参照できる
        childController.doSomething();
    }
}

子の FXML はこんな感じ。

<AnchorPane xmlns:fx="http://javafx.com/fxml" fx:controller="sample.ChildController" >
    <children>
        (略)
    </children>
</AnchorPane>

子のコントローラーは普通に作ればいいです。子のコントローラーが Initializable を実装している場合、親のコントローラーが初期化されるタイミングで子のコントローラーの initialize も呼ばれるようです((親子両方 Initializable の場合は子 -> 親の順で呼ばれるっぽい。考えてみれば当たり前ですが。))。

ていうか JavaFX-Gradle 使って JavaFX アプリケーション作ると NetBeans の新規ファイル作成ウィザードから JavaFX の項目消えるのはなんなんだ・・・。

*1:これは Scene Builder からはできないのかな?