FXMLドキュメントをJavaのクラスと結びつけて、イベントを処理したり値を取得したりすることが出来る。
このクラスをコントローラーと呼ぶ。
FXMLのルート要素のfx:controller属性でコントローラークラスを指定する。
コントローラークラスには、実装すべきインターフェースや親クラスは特に無い。
実行時(FXMLLoader#load()を呼び出した時)に自動的にこのクラスのインスタンスが作られる。
(クラスが見つからなければ、ClassNotFoundExceptionを原因とする例外が発生する)
ボタン押下のイベントを捕捉する例。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
<Pane fx:controller="example.Button1Controller" prefWidth="256" prefHeight="212" xmlns:fx="http://javafx.com/fxml">
<children>
<Button text="Click Me!" onAction="#handleButtonAction"/>
</children>
</Pane>
package example;
import javafx.event.ActionEvent;
public class Button1Controller {
public void handleButtonAction(ActionEvent event) {
System.out.println("Controller: button clicked");
}
}
ボタンタグのonAction属性でメソッド名を指定する。
ボタンが押されると、コントローラークラスのそのメソッドが呼ばれる。
コントローラークラスには特に親クラスは無く、したがってメソッドも何かをオーバーライドしたりはしない。
FXMLでTextField等の入力エリアを配置し、コントローラークラスでその値を読み書きすることが出来る。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
<VBox fx:controller="example.Button2Controller" prefWidth="256" prefHeight="212" xmlns:fx="http://javafx.com/fxml">
<children>
<TextField fx:id="text1" />
<Button text="Click Me!" onAction="#handleButtonAction"/>
<Label fx:id="label1" />
</children>
</VBox>
package example;
import javafx.event.ActionEvent; import javafx.scene.control.Label; import javafx.scene.control.TextField;
public class Button2Controller {
public TextField text1;
public Label label1;
public void handleButtonAction(ActionEvent event) {
String s = text1.getText();
label1.setText(s);
}
}
FXMLファイルのシーン(TextFieldやLabel)にfx:id属性で名前を付ける。
そして、コントローラークラスのフィールドとして、同名のpublicフィールドを用意する。
すると、コントローラークラス内のフィールドと表示されているコンポーネントの値が連動される。
コントローラークラスの初期化用のメソッドを用意することが出来る。
インスタンスが作られるとこのメソッドが呼ばれる。
コントローラークラスにInitializableインターフェースを実装し、initialize()メソッドを書く。
import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.Initializable;
public class Button2Controller implements Initializable {
〜
@Override
public void initialize(URL url, ResourceBundle resourcebundle) {
text1.setText("初期値");
label1.setText("ここにコピーされる");
}
〜
}
上記の例ではコントローラー内のフィールドやイベント処理用のメソッドをpublicにしていたが、public以外にしたい場合は@FXMLアノテーションを付ける。
ただし、リフレクションでsetAccessible(true)が呼ばれることになるので、SecurityManagerの設定によっては使えない。
import javafx.fxml.FXML;
public class Button2Controller {
@FXML private TextField text1;
@FXML private Label label1;
@FXML protected void handleButtonAction(ActionEvent event) {
〜
}
}
publicでないフィールドに@FXMLが付いていない場合は初期化時に例外(IllegalAccessException)が発生するが、
イベント用のメソッドだと、そのメソッドが呼び出される時に初めてチェックされるので、テスト漏れなど起こさぬよう注意が必要。
コントローラーのインスタンスはロード時に自動的に作られる。
FXMLLoaderインスタンスを使ってロードすれば、コントローラーインスタンスを取得できる。
URL file = new File("〜").toURI().toURL();
FXMLLoader loader = new FXMLLoader();
Parent root = (Parent) loader.load(file.openStream());
MyConroller controller = (MyController) loader.getController(); //古いJavaFX2ではキャストが必要