ページ

2013年3月31日日曜日

JavaFX - FXMLファイルでViewを定義したときのControllerの設定(1)

JavaFXで、ViewをFXMLファイルで定義したときの、Controllerの設定についてみていきます。

まず、以下のようなControllerを考えます。

public class SimpleViewController {
    @FXML
    private Parent root;

    public Parent getView() {
        return root;
    }
}

とりあえず、ルートのコントロールのみを取得するだけにしています。

このControllerをFXMLファイルで定義したViewに設定するには、FXMLファイルにfx:controller属性で指定します。

<StackPane xmlns:fx="http://javafx.com/fxml"
           fx:controller="SimpleViewController"
           fx:id="root">
    <Label text="Hello, world!"/>
</StackPane>

あとは、javafx.fxml.FXMLLoaderでFXMLファイルをロードした後、getControllerメソッドからfx:controller属性で指定したControllerクラスのインスタンスを取得します。

FXMLLoader loader = new FXMLLoader(simpleViewLocation);
loader.load();
SimpleViewController controller = (SimpleViewController)loader.getController();

上記では、FXMLファイルにControllerクラスを直接指定していますので、ViewがControllerに依存している形になっています。そこで、ViewがControllerに依存しないように、FXMLファイルに直接指定せずにControllerを設定してみたいと思います。

まず、FXMLファイルには次のようにfx:controller属性を指定しないようにします。

<StackPane xmlns:fx="http://javafx.com/fxml"
           fx:id="root">
    <Label text="Hello, world!"/>
</StackPane>

あとは、javafx.fxml.FXMLLoaderでFXMLファイルをロードする前に、setControllerメソッドでControllerクラスのインスタンスを設定します。

SimpleViewController controller = new SimpleViewController();
FXMLLoader loader = new FXMLLoader(simpleViewLocation);
loader.setController(controller);
loader.load();

この場合、FXMLファイルに直接fx:controller属性を指定しませんので、ViewがControllerに依存せずに、Controllerを設定できます。また、Controllerのインスタンスを指定しますので、他の依存するオブジェクトをControllerに注入したい場合にも利用できます。

次に、ControllerクラスをFXMLファイルで定義しているルート要素のコントロールとして定義する場合についてみていきます。

まず、FXMLファイルでViewを定義します。このとき、ルート要素はfx:rootとして定義します。

<fx:root xmlns:fx="http://javafx.com/fxml" type="StackPane">
    <Label text="Hello, world!"/>
</fx:root>

type属性に、ルートとなるコントロールの型を指定します。

次に、type属性で指定した型を継承して、Controllerクラスを定義します。

public class SimpleControl extends StackPane {
    private static final URL VIEW_LOCATION = SimpleControl.class.getResource("SimpleControl.fxml");
    
    public SimpleControl() {
        initializeComponent();
    }

    protected void initializeComponent() {
        FXMLLoader loader = new FXMLLoader(VIEW_LOCATION);
        loader.setController(this);
        loader.setRoot(this);
        try {
            loader.load();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

javafx.fxml.FXMLLoaderでFXMLファイルをロードする前に、setControllerメソッドとsetRootメソッドにControllerクラスのインスタンスを設定します。

上記で定義したControllerをシーンに設定してみます。

Scene scene = SceneBuilder.create()
    .root(new SimpleControl())
    .build();

上記のように設定した場合は、.NETでいうコードビハインドのような感じになります。

FXMLファイルでViewを定義した場合、Controllerの設定には、基本的に上記の2番目か3番目の方法で行うことになると思います。3番目の方法の場合は、Controllerというよりは、カスタムコントロールという位置づけになり、他のFXMLファイルにそのまま要素として指定することができますので、.NETで開発したことがある人には、馴染みやすいかもしれません。

FXMLファイルでViewを定義した場合は、javafx.fxml.FXMLLoaderクラスを使ってFXMLファイルを読み込むのですが、同じような記述を毎回行うことになりますので、次回は、FXMLファイルを読み込んでControllerを取得するユーティリティクラスを作成してみたいと思います。