javafx
FXMLとコントローラ
サーチ…
構文
- xmlns:fx = " http://javafx.com/fxml " //名前空間宣言
FXMLの例
ボタンとラベルノードを含むAnchorPane
概要を示す簡単なFXMLドキュメント:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.FXMLDocumentController">
<children>
<Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
<Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
</children>
</AnchorPane>
このFXMLファイルの例は、コントローラクラスに関連付けられています。この場合のFXMLとコントローラクラスの関連付けは、クラス名をFXMLのルート要素のfx:controller
属性の値として指定することによって行われます。fx fx:controller="com.example.FXMLDocumentController"
コントローラクラスを使用すると、FXMLファイルで定義されているUI要素のユーザーアクションに応じて、Javaコードを実行できます。
package com.example ;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class FXMLDocumentController {
@FXML
private Label label;
@FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
label.setText("Hello World!");
}
@Override
public void initialize(URL url, ResourceBundle resources) {
// Initialization code can go here.
// The parameters url and resources can be omitted if they are not needed
}
}
FXMLLoader
を使用してFXMLファイルをロードすることができます:
public class MyApp extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
load
メソッドはいくつかのアクションを実行し、発生する順序を理解すると便利です。この単純な例では、
FXMLLoader
は、FXMLファイルを読み込んで解析します。ファイルに定義されている要素に対応するオブジェクトを作成し、定義されているfx:id
属性を記録します。FXMLファイルのルート要素は
fx:controller
属性を定義しているため、FXMLLoader
は指定するクラスの新しいインスタンスを作成します。デフォルトでは、これは指定されたクラスで引数のないコンストラクタを呼び出すことによって行われます。フィールド名が一致し、
public
(推奨されない)または注釈付き@FXML
(推奨)のいずれかであるフィールドをコントローラーに持つfx:id
属性の要素は、対応するフィールドに「注入」されます。この例ではそう、そこにあるので、Label
とFXMLファイル内fx:id="label"
として定義され、コントローラ内とフィールドが@FXML private Label label ;
label
フィールドは、FXMLLoader
によって作成されたLabel
インスタンスで初期化されます。イベントハンドラは、
onXXX="#..."
プロパティが定義されたFXMLファイルの要素に登録されます。これらのイベントハンドラは、コントローラクラス内の指定されたメソッドを呼び出します。この例では、Button
はonAction="#handleButtonAction"
があり、コントローラはメソッドを定義しているため@FXML private void handleButtonAction(ActionEvent event) { ... }
ユーザーがボタンを押すなどのアクションがボタンで発生すると、このメソッドが呼び出されます。このメソッドは
void
戻り型でなければならず、イベント型(この例ではActionEvent
に一致するパラメータを定義するか、またはパラメータを定義することができません。最後に、コントローラクラスが
initialize
メソッドを定義している場合、このメソッドが呼び出されます。これは@FXML
フィールドが挿入された後に発生するので、このメソッドで安全にアクセスでき、FXMLファイルの要素に対応するインスタンスで初期化されます。initialize()
メソッドは、パラメータを取らないか、URL
とResourceBundle
とることができURL
。後者の場合には、これらのパラメータは、によって移入されるURL
FXMLファイルの場所を表す、および任意ResourceBundle
上に設定FXMLLoader
介しloader.setResources(...)
これらのいずれかが設定されていない場合、null
なる可能性がありnull
。
ネストされたコントローラ
単一のコントローラを使用して、単一のFXMLにUI全体を作成する必要はありません。
<fx:include>
タグを使用して、1つのfxmlファイルを別のファイルに含めることができます。インクルードされたfxmlのコントローラは、 FXMLLoader
によって作成された他のオブジェクトと同様に、インクルードファイルのコントローラに注入できます。
これは、 fx:id
属性を<fx:include>
要素に追加することによって行われます。このようにして、インクルードされたfxmlのコントローラは、名前<fx:id value>Controller
フィールドに注入され<fx:id value>Controller
。
例:
fx:id値 | 注射のフィールド名 |
---|---|
foo | fooController |
答え42 | 答え42コントローラ |
xYz | xYzController |
サンプルfxmls
カウンタ
これはText
ノードを持つStackPane
を含むfxmlです。このfxmlファイルのコントローラは、現在のカウンタ値の取得とカウンタのインクリメントを許可します。
counter.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<StackPane prefHeight="200" prefWidth="200" xmlns:fx="http://javafx.com/fxml/1" fx:controller="counter.CounterController">
<children>
<Text fx:id="counter" />
</children>
</StackPane>
CounterController
package counter;
import javafx.fxml.FXML;
import javafx.scene.text.Text;
public class CounterController {
@FXML
private Text counter;
private int value = 0;
public void initialize() {
counter.setText(Integer.toString(value));
}
public void increment() {
value++;
counter.setText(Integer.toString(value));
}
public int getValue() {
return value;
}
}
fxmlを含む
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane prefHeight="500" prefWidth="500" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="counter.OuterController">
<left>
<Button BorderPane.alignment="CENTER" text="increment" onAction="#increment" />
</left>
<center>
<!-- content from counter.fxml included here -->
<fx:include fx:id="count" source="counter.fxml" />
</center>
</BorderPane>
OuterController
インクルードされたfxmlのコントローラーがこのコントローラーに注入されます。ここでは、 Button
onAction
イベントのハンドラを使用してカウンタをインクリメントします。
package counter;
import javafx.fxml.FXML;
public class OuterController {
// controller of counter.fxml injected here
@FXML
private CounterController countController;
public void initialize() {
// controller available in initialize method
System.out.println("Current value: " + countController.getValue());
}
@FXML
private void increment() {
countController.increment();
}
}
コードがouter.fxml
と同じパッケージ内のクラスから呼び出されたと仮定すると、fxmlsは次のようにロードできます。
Parent parent = FXMLLoader.load(getClass().getResource("outer.fxml"));
ブロックを定義する
場合によっては、fxml内の通常のオブジェクト構造の外部で要素を作成する必要があることがあります。
これはDefine Blocksが有効になる場所です:
<fx:define>
要素内の内容は、親要素用に作成されたオブジェクトには追加されません。
<fx:define>
すべての子要素には、 fx:id
属性が必要です。
この方法で作成されたオブジェクトは、 <fx:reference>
要素を使用するか、または式バインディングを使用して後で参照できます。
<fx:reference>
要素が持つ任意の要素を参照するために使用することができるfx:id
前処理された属性<fx:reference>
と同じ値を使用して要素が処理されfx:id
で参照される要素の属性<fx:reference>
要素のsource
属性。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml/1" prefHeight="300.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8">
<children>
<fx:define>
<String fx:value="My radio group" fx:id="text" />
</fx:define>
<Text>
<text>
<!-- reference text defined above using fx:reference -->
<fx:reference source="text"/>
</text>
</Text>
<RadioButton text="Radio 1">
<toggleGroup>
<ToggleGroup fx:id="group" />
</toggleGroup>
</RadioButton>
<RadioButton text="Radio 2">
<toggleGroup>
<!-- reference ToggleGroup created for last RadioButton -->
<fx:reference source="group"/>
</toggleGroup>
</RadioButton>
<RadioButton text="Radio 3" toggleGroup="$group" />
<!-- reference text defined above using expression binding -->
<Text text="$text" />
</children>
</VBox>
FXMLにデータを渡す - 既存のコントローラにアクセスする
問題:一部のデータは、fxmlからロードされたシーンに渡す必要があります。
溶液
fx:controller
属性を使用してコントローラを指定し、 FXMLLoader
ロードに使用されるFXMLLoader
インスタンスからロードプロセス中に作成されたコントローラインスタンスを取得します。
データをコントローラインスタンスに渡し、それらのメソッドでデータを処理するメソッドを追加します。
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="valuepassing.TestController">
<children>
<Text fx:id="target" />
</children>
</VBox>
コントローラ
package valuepassing;
import javafx.fxml.FXML;
import javafx.scene.text.Text;
public class TestController {
@FXML
private Text target;
public void setData(String data) {
target.setText(data);
}
}
fxmlのロードに使用されるコード
String data = "Hello World!";
FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));
Parent root = loader.load();
TestController controller = loader.<TestController>getController();
controller.setData(data);
FXMLへのデータの受け渡し - コントローラインスタンスの指定
問題:一部のデータは、fxmlからロードされたシーンに渡す必要があります。
溶液
後でfxmlをロードするために使用するFXMLLoader
インスタンスを使用してコントローラを設定します。
fxmlをロードする前に、コントローラに関連するデータが格納されていることを確認してください。
注意:この場合、fxmlファイルにはfx:controller
属性を含めないでください。
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml/1">
<children>
<Text fx:id="target" />
</children>
</VBox>
コントローラ
import javafx.fxml.FXML;
import javafx.scene.text.Text;
public class TestController {
private final String data;
public TestController(String data) {
this.data = data;
}
@FXML
private Text target;
public void initialize() {
// handle data once the fields are injected
target.setText(data);
}
}
fxmlのロードに使用されるコード
String data = "Hello World!";
FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));
TestController controller = new TestController(data);
loader.setController(controller);
Parent root = loader.load();
パラメータをFXMLに渡す - controllerFactoryを使用する
問題:一部のデータは、fxmlからロードされたシーンに渡す必要があります。
溶液
コントローラの作成を担当するコントローラファクトリを指定します。ファクトリで作成されたコントローラインスタンスにデータを渡します。
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="valuepassing.TestController">
<children>
<Text fx:id="target" />
</children>
</VBox>
コントローラ
package valuepassing;
import javafx.fxml.FXML;
import javafx.scene.text.Text;
public class TestController {
private final String data;
public TestController(String data) {
this.data = data;
}
@FXML
private Text target;
public void initialize() {
// handle data once the fields are injected
target.setText(data);
}
}
fxmlのロードに使用されるコード
文字列データ= "Hello World!";
Map<Class, Callable<?>> creators = new HashMap<>();
creators.put(TestController.class, new Callable<TestController>() {
@Override
public TestController call() throws Exception {
return new TestController(data);
}
});
FXMLLoader loader = new FXMLLoader(getClass().getResource("test.fxml"));
loader.setControllerFactory(new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> param) {
Callable<?> callable = creators.get(param);
if (callable == null) {
try {
// default handling: use no-arg constructor
return param.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
} else {
try {
return callable.call();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}
});
Parent root = loader.load();
これは複雑に見えるかもしれませんが、fxmlが必要とするコントローラークラスを決定できる場合は便利です。
FXMLによるインスタンスの作成
次のクラスを使用して、クラスのインスタンスを作成する方法を示します。
Person(@NamedArg("name") String name)
アノテーションは、 @NamedArg
アノテーションが使用できないため削除する@NamedArg
があります。
package fxml.sample;
import javafx.beans.NamedArg;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
public static final Person JOHN = new Person("John");
public Person() {
System.out.println("Person()");
}
public Person(@NamedArg("name") String name) {
System.out.println("Person(String)");
this.name.set(name);
}
public Person(Person person) {
System.out.println("Person(Person)");
this.name.set(person.getName());
}
private final StringProperty name = new SimpleStringProperty();
public final String getName() {
System.out.println("getter");
return this.name.get();
}
public final void setName(String value) {
System.out.println("setter");
this.name.set(value);
}
public final StringProperty nameProperty() {
System.out.println("property getter");
return this.name;
}
public static Person valueOf(String value) {
System.out.println("valueOf");
return new Person(value);
}
public static Person createPerson() {
System.out.println("createPerson");
return new Person();
}
}
fxmlをロードする前に、 Person
クラスがすでに初期化されているものとします。
輸入に関する注意
次のfxmlの例では、importsセクションは除外されます。しかし、fxmlは
<?xml version="1.0" encoding="UTF-8"?>
fxmlファイルで使用されるすべてのクラスをインポートするimportsセクションが続きます。これらのインポートは、非静的インポートに似ていますが、処理指示として追加されます。 java.lang
パッケージのクラスもインポートする必要がありjava.lang
。
この場合、次のインポートを追加する必要があります。
<?import java.lang.*?>
<?import fxml.sample.Person?>
@NamedArg
注釈付きコンストラクタ
すべてのパラメータを用いて注釈されているコンストラクタがある場合@NamedArg
との全ての値@NamedArg
注釈はFXML中に存在する、コンストラクタは、これらのパラメータと一緒に使用されるであろう。
<Person name="John"/>
<Person xmlns:fx="http://javafx.com/fxml">
<name>
<String fx:value="John"/>
</name>
</Person>
両方がロードされた場合、次のコンソール出力が得られます。
Person(String)
argsコンストラクタなし
適切な@NamedArg
注釈付きコンストラクタがない場合、パラメータを取らないコンストラクタが使用されます。
@NamedArg
アノテーションをコンストラクタから削除し、読み込みを試みます。
<Person name="John"/>
これは、パラメータなしでコンストラクタを使用します。
出力:
Person()
setter
fx:value
属性
fx:value
属性を使用して、その値をstatic
valueOf
メソッドに渡すことができますfx:value
このメソッドは、 String
パラメータを使用し、使用するインスタンスを返します。
例
<Person xmlns:fx="http://javafx.com/fxml" fx:value="John"/>
出力:
valueOf
Person(String)
fx:factory
fx:factory
属性を使用すると、パラメータを取らない任意のstatic
メソッドを使用してオブジェクトを作成できます。
例
<Person xmlns:fx="http://javafx.com/fxml" fx:factory="createPerson">
<name>
<String fx:value="John"/>
</name>
</Person>
出力:
createPerson
Person()
setter
<fx:copy>
fx:copy
コンストラクタを呼び出すことができます。別のfx:id
指定タグのsource
属性は、そのオブジェクトをパラメータとして持つコピーコンストラクタを呼び出します。
例:
<ArrayList xmlns:fx="http://javafx.com/fxml">
<Person fx:id="p1" fx:constant="JOHN"/>
<fx:copy source="p1"/>
</ArrayList>
出力
Person(Person)
getter
fx:constant
fx:constant
はstatic final
フィールドから値を得ることを可能にします。
例
<Person xmlns:fx="http://javafx.com/fxml" fx:constant="JOHN"/>
クラスを初期化するときに作成されたJOHN
を参照するだけなので、出力は生成されません。
プロパティの設定
fxmlのオブジェクトにデータを追加する方法はいくつかあります。
<property>
タグ
プロパティの名前を持つタグは、インスタンスの作成に使用される要素の子として追加できます。このタグの子は、セッターを使用してプロパティに割り当てられるか、プロパティの内容に追加されます(読み取り専用のリスト/マップのプロパティ)。
デフォルトプロパティ
クラスに@DefaultProperty
注釈を付けることができます。この場合、要素は、プロパティの名前を持つ要素を使用せずに、子要素として直接追加できます。
property="value"
属性
属性名として属性名を使用し、属性値として値を使用してプロパティを割り当てることができます。これは、タグの子として次の要素を追加するのと同じ効果があります。
<property>
<String fx:value="value" />
</property>
静的セッター
プロパティはstatic
セッターを使用しstatic
設定することもできます。これらはsetProperty
という名前のstatic
メソッドで、要素を第1のパラメータとして、値を第2のパラメータとして設定します。これらのメソッドはどのクラスでも使用でき、通常のプロパティ名の代わりにContainingClass.property
使用して使用できます。
注:現在、値の型がString
ない限り、対応する静的ゲッターメソッド(つまり、静的メソッド( getProperty
という静的メソッドを静的セッターと同じクラスのパラメーターとして要素を取得する静的メソッド)を使用する必要があります。
型強制
次のメカニズムは、例えばセッターメソッドのパラメーター型に合うように、割り当て中に正しいクラスのオブジェクトを取得するために使用されます。
クラスが代入可能な場合は、値そのものが使用されます。
それ以外の場合、値は次のように変換されます。
ターゲットタイプ | 使用される値(ソース値s ) |
---|---|
Boolean 、 boolean | Boolean.valueOf(s) |
char 、 Character | s.toString.charAt(0) |
その他のプリミティブ型またはラッパー型 | ターゲット型の適切なメソッドvalueOf(s.toString()) Number s 場合は、ラッパー型のvalueOf(s.toString()) |
BigInteger | BigInteger.valueOf(s.longValue()) であるs でNumber 、 new BigInteger(s.toString()) それ以外の場合は |
BigDecimal | BigDecimal.valueOf(s.doubleValue()) であるs でNumber 、 new BigDecimal(s.toString()) それ以外の場合は |
数 | Double.valueOf(s.toString()) の場合s.toString() 含まれてい. 、 Long.valueOf(s.toString()) それ以外の場合 |
Class | クラスを初期化せずに現在のスレッドのコンテキストClassLoader を使用して呼び出されたClass.forName(s.toString()) |
列挙型 | valueOf メソッドの結果は、 s がそれぞれ小文字で始まるString であれば、大文字の前に挿入された_ 区切られたすべての大文字のString に追加変換されます。 |
その他 | 返される値static valueOf タイプのマッチングパラメータ有したtargetTypeの方法において、 s またはそのタイプのスーパークラスを |
注:この動作は十分に文書化されておらず、変更される可能性があります。
例
public enum Location {
WASHINGTON_DC,
LONDON;
}
package fxml.sample;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.beans.DefaultProperty;
@DefaultProperty("items")
public class Sample {
private Location loaction;
public Location getLoaction() {
return loaction;
}
public void setLoaction(Location loaction) {
this.loaction = loaction;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
int number;
private final List<Object> items = new ArrayList<>();
public List<Object> getItems() {
return items;
}
private final Map<String, Object> map = new HashMap<>();
public Map<String, Object> getMap() {
return map;
}
private BigInteger serialNumber;
public BigInteger getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(BigInteger serialNumber) {
this.serialNumber = serialNumber;
}
@Override
public String toString() {
return "Sample{" + "loaction=" + loaction + ", number=" + number + ", items=" + items + ", map=" + map + ", serialNumber=" + serialNumber + '}';
}
}
package fxml.sample;
public class Container {
public static int getNumber(Sample sample) {
return sample.number;
}
public static void setNumber(Sample sample, int number) {
sample.number = number;
}
private final String value;
private Container(String value) {
this.value = value;
}
public static Container valueOf(String s) {
return new Container(s);
}
@Override
public String toString() {
return "42" + value;
}
}
以下のfxml
ファイルを読み込んだ結果をfxml
すると、
Sample{loaction=WASHINGTON_DC, number=5, items=[42a, 42b, 42c, 42d, 42e, 42f], map={answer=42, g=9.81, hello=42A, sample=Sample{loaction=null, number=33, items=[], map={}, serialNumber=null}}, serialNumber=4299}
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import fxml.sample.*?>
<Sample xmlns:fx="http://javafx.com/fxml/1" Container.number="5" loaction="washingtonDc">
<!-- set serialNumber property (type coercion) -->
<serialNumber>
<Container fx:value="99"/>
</serialNumber>
<!-- Add elements to default property-->
<Container fx:value="a"/>
<Container fx:value="b"/>
<Container fx:value="c"/>
<Container fx:value="d"/>
<Container fx:value="e"/>
<Container fx:value="f"/>
<!-- fill readonly map property -->
<map g="9.81">
<hello>
<Container fx:value="A"/>
</hello>
<answer>
<Container fx:value=""/>
</answer>
<sample>
<Sample>
<!-- static setter-->
<Container.number>
<Integer fx:value="33" />
</Container.number>
</Sample>
</sample>
</map>
</Sample>