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ではキャストが必要