リリース: JavaFX 8.0

FXMLの概要

最終更新日: 2013/9/10

目次

概要

FXMLは、Javaオブジェクト・グラフを構築するためのスクリプト可能なXMLベースのマークアップ言語です。そのようなグラフを手続き型のコードで構築する方法の便利な代替方法であり、JavaFXアプリケーションのユーザー・インタフェースの定義に最適です。これは、XMLドキュメントの階層構造が、JavaFXシーン・グラフの構造と密接に対応しているためです。

このドキュメントでは、FXMLマークアップ言語の概要を示し、これを使用してJavaFXアプリケーションの開発をどのように簡素化できるかについて説明します。

要素

FXMLでは、XML要素は次のいずれかを表します。

クラス・インスタンス、インスタンス・プロパティ、静的プロパティおよびdefineブロックについては、この項で後述します。スクリプトについては、後の項で説明します。

クラス・インスタンス要素

クラス・インスタンスは、FXMLで複数の方法で構築できます。最も一般的には、インスタンス宣言要素を使用します。この場合、単純に名前でクラスの新しいインスタンスが作成されます。クラス・インスタンスを作成するその他の方法には、既存の値の参照、既存の値のコピー、外部FXMLファイルの読込みなどがあります。それぞれの方法については、次に詳しく説明します。

インスタンス宣言

要素のタグは、インスタンス宣言とみなされる場合、およびそのタグが大文字で始まる(かつクラスがインポートされている)場合またはJavaでの場合と同様に、タグはクラスの完全修飾(パッケージ名を含む)名を示します。FXMLローダー(後述)は、このような要素を検出すると、そのクラスのインスタンスを作成します。

クラスのインポートは、import処理命令(PI)を使用して実行されます。たとえば、次のPIでは、javafx.scene.control.Labelクラスが現在のFXMLドキュメントのネームスペースにインポートされます。

<?import javafx.scene.control.Label?>

このPIでは、javafx.scene.controlパッケージからすべてのクラスが現在のネームスペースにインポートされます。

<?import javafx.scene.control.*?>

JavaBeanコンストラクタおよびプロパティのネーミング規則に準拠するクラスは、FXMLを使用して簡単にインスタンス化および構成できます。次の単純ですが完全な例では、javafx.scene.control.Labelのインスタンスを作成し、そのtextプロパティをHello, World!に設定しています。

<?import javafx.scene.control.Label?>
<Label text="Hello, World!"/>

この例で、Labelのtextプロパティは、XML属性を使用して設定されます。プロパティは、ネストされたプロパティ要素を使用して設定することもできます。プロパティ要素については、この項の後半で詳しく説明します。プロパティ属性については、後の項で説明します。

Bean規則に準拠しないクラスも、ビルダーと呼ばれるオブジェクトを使用してFXMLで構築できます。ビルダーについては、後で詳しく説明します。

マップ

内部的に、FXMLローダーはcom.sun.javafx.fxml.BeanAdapterのインスタンスを使用して、インスタンス化されたオブジェクトをラップし、そのセッター・メソッドを呼び出します。このprivate (現在)クラスはjava.util.Mapインタフェースを実装し、コール元がBeanプロパティ値をキー/値ペアとして取得および設定できるようにします。

要素がMap (java.util.HashMapなど)をすでに実装した型を表す場合、これはラップされず、そのget()およびput()メソッドが直接呼び出されます。たとえば、次のFXMLではHashMapのインスタンスが作成され、そのfooおよびbarの値が123と456にそれぞれ設定されます。

<HashMap foo="123" bar="456"/>
fx:value

fx:value属性を使用して、デフォルト・コンストラクタを持たないが、static valueOf(String)メソッドを提供する型のインスタンスを初期化できます。たとえば、java.lang.Stringおよび各プリミティブ・ラッパー型はvalueOf()メソッドを定義し、FXMLで次のように構築できます。

<String fx:value="Hello, World!"/>
<Double fx:value="1.0"/>
<Boolean fx:value="false"/>

static valueOf(String)メソッドを定義するカスタム・クラスもこの方法で構築できます。

fx:factory

fx:factory属性は、クラスがデフォルト・コンストラクタを持たないオブジェクトを作成する別の方法です。この属性の値は、クラス・インスタンスを作成するための、引数なしのstaticファクトリ・メソッドの名前です。たとえば、次のマークアップは、3つの文字列値が移入された監視可能な配列リストのインスタンスを作成します。

<FXCollections fx:factory="observableArrayList">
    <String fx:value="A"/>
    <String fx:value="B"/>
    <String fx:value="C"/>
</FXCollections>
ビルダー

Bean規則に準拠しないクラス(不変値を表すものなど)のインスタンスを作成する3番目の方法は、ビルダーです。ビルダーのデザイン・パターンは、不変型のインスタンスを生成する可変ヘルパー・クラス(ビルダーと呼ばれる)にオブジェクトの構築を委譲します。

FXMLにおけるビルダーのサポートは、2つのインタフェースによって提供されます。javafx.util.Builderインタフェースは、実際のオブジェクトを構築するbuild()という名前の単一のメソッドを定義します。

public interface Builder<T> {
    public T build();
}

javafx.util.BuilderFactoryは、指定された型をインスタンス化できるビルダーを生成します。

public interface BuilderFactory {
    public Builder<?> getBuilder(Class<?> type);
}

デフォルト・ビルダー・ファクトリのJavaFXBuilderFactoryは、javafx.fxmlパッケージで提供されます。このファクトリは、ほとんどの不変JavaFX型を作成および構成できます。たとえば、次のマークアップは、デフォルト・ビルダーを使用して、不変javafx.scene.paint.Colorクラスのインスタンスを作成します。

<Color red="1.0" green="0.0" blue="0.0"/>

要素の開始タグの処理時に作成されるBeanタイプとは異なり、ビルダーによって作成されるオブジェクトは、要素の終了タグに到達するまでインスタンス化されません。これは、要素が完全に処理されるまで、必要なすべての引数が利用できない場合があるためです。たとえば、前の例のColorオブジェクトは、次のように記述することもできます。

<Color>
    <red>1.0</red>
    <green>0.0</green>
    <blue>0.0</blue>
</Color>

Colorインスタンスは、3つの色の構成要素がすべて認識されるまで完全には作成されません。

ビルダーによって作成されるオブジェクトのマークアップを処理する場合、Builderインスタンスは値オブジェクトとして処理され、BuilderMapインタフェースを実装する場合、put()メソッドを使用してビルダーの属性値が設定されます。それ以外の場合、ビルダーはBeanAdapterでラップされ、そのプロパティは標準Beanセッターを介して公開されると想定されます。

<fx:include>

<fx:include>タグは、別のファイルで定義されたFXMLマークアップからオブジェクトを作成します。次のように使用されます。

<fx:include source="filename"/>

filenameは、読み込むFXMLファイルの名前です。先頭にスラッシュ文字が付いている値は、クラスパスに相対的であるとみなされます。先頭にスラッシュが付いていない値は、現在のドキュメントのパスに相対的であるとみなされます。

たとえば、次のマークアップを考えてみます。

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns:fx="http://javafx.com/fxml">
    <children>
        <fx:include source="my_button.fxml"/>
    </children>
</VBox>

my_button.fxmlに次が含まれるとします。

<?import javafx.scene.control.*?>
<Button text="My Button"/>

この場合、生成されるシーン・グラフには、子ノードとして単一のButtonを持つルート・オブジェクトとしてVBoxが含まれます。

fxネームスペース接頭辞が使用されていることに注意してください。これは、FXMLソース・ファイルの内部処理に使用される要素と属性の数を定義する予約済接頭辞です。通常、これはFXMLドキュメントのルート要素で宣言されます。fxネームスペースによって提供されるその他の機能については、以降の項で説明します。

<fx:include>は、読み込まれるコンテンツのローカライズに使用されるリソース・バンドルの名前を指定する属性、およびソース・ファイルのエンコードに使用される文字セットもサポートします。リソース解決については、後の項で説明します。

<fx:include source="filename" resources="resource_file" charset="utf-8"/>

<fx:constant>

<fx:constant>要素は、クラス定数への参照を作成します。たとえば、次のマークアップは、ButtonインスタンスのminWidthプロパティの値を、java.lang.Doubleクラスで定義されたNEGATIVE_INFINITY定数の値に設定します。

<Button>
    <minHeight><Double fx:constant="NEGATIVE_INFINITY"/></minHeight>
</Button>

<fx:reference>

<fx:reference>要素は、既存の要素への新しい参照を作成します。このタグが出現する場所では、要素は実際には必ず名前付き要素の値に置換されます。これはfx:id属性またはスクリプト変数とともに使用されます。どちらについても後の項で詳しく説明します。<fx:reference>要素のsource属性は、新しい要素が参照するオブジェクトの名前を指定します。

たとえば、次のマークアップは、前に定義したmyImageという名前のImageインスタンスをImageViewコントロールのimageプロパティに割り当てます。

<ImageView>
    <image>
        <fx:reference source="myImage"/>
    </image>
</ImageView>

属性変数解決演算子を使用して変数を逆参照することもできますが(後で「属性」の項で説明します)、fx:referenceは、通常、コレクションに参照を追加する場合など、参照値を要素として指定する必要がある場合にのみ使用されます。

<ArrayList>
    <fx:reference source="element1"/>
    <fx:reference source="element2"/>
    <fx:reference source="element3"/>
</ArrayList>

他のほとんどの場合は、属性を使用する方がより簡単で簡潔です。

<fx:copy>

<fx:copy>要素は、既存の要素のコピーを作成します。<fx:reference>と同様に、fx:id属性またはスクリプト変数とともに使用されます。要素のsource属性は、コピー元オブジェクトの名前を指定します。ソース・タイプは、ソース値からコピーを作成するために使用されるコピー・コンストラクタを定義する必要があります。

現在、このようなコピー・コンストラクタを提供するJavaFXプラットフォーム・クラスは存在しないため、この要素は主にアプリケーション開発者向けに提供されています。これは将来のリリースで変更される可能性があります。

<fx:root>

<fx:root>要素は、以前に定義されたルート要素への参照を作成します。これは、FXMLドキュメントのルート・ノードとしてのみ有効です。<fx:root>は、FXMLマークアップに基づくカスタム・コントロールを作成する場合に主に使用されます。詳細は、「FXMLLoader」の項で説明します。

プロパティ要素

タグ名が小文字で始まる要素は、オブジェクト・プロパティを表します。プロパティ要素は次のいずれかを表します。

プロパティ・セッター

要素がプロパティ・セッターを表す場合、要素(テキスト・ノードまたはネストされたクラス・インスタンス要素である必要があります)のコンテンツは、値としてプロパティのセッターに渡されます。

たとえば、次のFXMLは、Labelクラスのインスタンスを作成し、ラベルのtextプロパティの値をHello, World!に設定します。

<?import javafx.scene.control.Label?>
<Label>
    <text>Hello, World!</text>
</Label>

これにより、結果は、属性を使用してtextプロパティを設定した以前の例と同じになります。

<?import javafx.scene.control.Label?>
<Label text="Hello, World!"/>

通常、プロパティ要素は、プロパティ値が単純な文字列ベースの属性値を使用して表すことができない複合型の場合、または値の文字列長が非常に長いため、属性として指定すると読みにくくなる場合に使用されます。

型の強制

FXMLでは、型の強制を使用して、必要に応じてプロパティ値を適切な型に変換します。XMLでサポートされているデータ型は要素、テキストおよび(値がテキストの)属性のみであるため、型の強制が必要となります。ただし、Javaでは、組込みプリミティブ値型および拡張可能参照型を含む多数の異なるデータ型がサポートされています。

FXMLローダーは、BeanAdaptercoerce()メソッドを使用して必要な型変換を実行します。このメソッドは、Stringからbooleanまたはintからdoubleなどの基本のプリミティブ型変換を実行でき、StringからClassまたはStringからEnumへの変換も行います。ターゲット型でstatic valueOf()メソッドを定義して、追加の変換を実装できます。

読取り専用リスト・プロパティ

読取り専用リスト・プロパティは、ゲッターがjava.util.Listのインスタンスを返し、対応するセッター・メソッドを持たないBeanプロパティです。読取り専用リスト要素のコンテンツは、処理時に自動的にリストに追加されます。

たとえば、javafx.scene.Groupのchildrenプロパティは、グループの子ノードを表す読取り専用リスト・プロパティです。

<?import javafx.scene.*?>
<?import javafx.scene.shape.*?>
<Group xmlns:fx="http://javafx.com/fxml">
    <children>
        <Rectangle fx:id="rectangle" x="10" y="10" width="320" height="240"
            fill="#ff0000"/>
        ...
    </children>
</Group>

<children>要素の各サブ要素は、読み取られるたびにGroup#getChildren()によって返されるリストに追加されます。

読取り専用マップ・プロパティ

読取り専用マップ・プロパティは、ゲッターがjava.util.Mapのインスタンスを返し、対応するセッター・メソッドを持たないbeanプロパティです。読取り専用マップ要素の属性は、終了タグの処理時にマップに適用されます。

javafx.scene.Nodeのpropertiesプロパティは、読取り専用マップ・プロパティの例です。次のマークアップは、Labelインスタンスのfooおよびbarプロパティを、123と456にそれぞれ設定します。

<?import javafx.scene.control.*?>
<Button>
    <properties foo="123" bar="456"/>
</Button>

型がListでもMapでもない読取り専用プロパティは、読取り専用マップとして処理されます。ゲッター・メソッドの戻り値はBeanAdapterでラップされ、他の読取り専用マップと同様に使用できます。

デフォルト・プロパティ

クラスでは、javafx.beansパッケージで定義されている@DefaultProperty注釈を使用してデフォルト・プロパティを定義できます。デフォルト・プロパティを表すサブ要素が存在する場合、マークアップから省略できます。

たとえば、javafx.scene.layout.Pane (javafx.scene.layout.VBoxのスーパークラス)では子のデフォルト・プロパティが定義されるため<children>要素は不要で、ローダーによってVBoxのサブ要素がコンテナの子コレクションに自動的に追加されます。

<?import javafx.scene.*?>
<?import javafx.scene.shape.*?>
<VBox xmlns:fx="http://javafx.com/fxml">
    <Button text="Click Me!"/>
    ...
</VBox>

デフォルト・プロパティは、コレクションに限定されません。要素のデフォルト・プロパティがスカラー値を参照する場合、その要素のサブ要素はプロパティの値として設定されます。

たとえば、javafx.scene.control.ScrollPaneではコンテンツのデフォルト・プロパティが定義されるため、コンテンツとしてTextAreaを含むスクロール・ペインを次のように指定できます。

<ScrollPane>
    <TextArea text="Once upon a time..."/>
</ScrollPane>

デフォルト・プロパティを利用すると、FXMLマークアップの冗長度を大幅に低下させることができます。

静的プロパティ

要素は、静的プロパティ(アタッチされたプロパティと呼ばれることもある)を表す場合もあります。静的プロパティは、特定のコンテキストでのみ意味を持つプロパティです。静的プロパティは適用先のクラスに本来存在するものではありませんが、別のクラスによって定義されます(多くの場合、コントロールの親コンテナ)。

静的プロパティには、静的プロパティを定義するクラス名の接頭辞が付けられます。たとえば、次のFXMLは、GridPaneのrowIndexおよびcolumnIndexプロパティの静的セッターを呼び出します。

<GridPane>
    <children>
        <Label text="My Label">
            <GridPane.rowIndex>0</GridPane.rowIndex>
       <GridPane.columnIndex>0</GridPane.columnIndex>
        </Label>
    </children>
</TabPane>

これは、Javaでおおむね次のように変換されます。

GridPane gridPane = new GridPane();

Label label = new Label();
label.setText("My Label");

GridPane.setRowIndex(label, 0);
GridPane.setColumnIndex(label, 0);

gridPane.getChildren().add(label);
GridPane#setRowIndex()およびGridPane#setColumnIndex()へのコールは、Labelインスタンスに索引データをアタッチします。GridPaneでは、レイアウト時にこれらを使用して子を適切に配置します。AnchorPaneBorderPaneStackPaneなどの他のコンテナは、同様のプロパティを定義します。

インスタンス・プロパティと同様に、静的プロパティ要素は、通常、プロパティ値を属性値で効率的に表すことができない場合に使用されます。それ以外の場合は、一般に、静的プロパティ属性(後の項で説明します)によってより簡潔で読みやすいマークアップが生成されます。

defineブロック

<fx:define>要素は、オブジェクト階層の外部に存在するが、他の場所で参照する必要があるオブジェクトを作成するために使用されます。

たとえば、ラジオ・ボタンを使用する場合は、一般的に、ボタンの選択状態を管理するToggleGroupを定義します。このグループはシーン・グラフ自体の一部ではないため、ボタンの親に追加することはできません。定義ブロックを使用すると、ドキュメントの構造全体に影響を与えることなくボタン・グループを作成できます。

<VBox>
    <fx:define>
        <ToggleGroup fx:id="myToggleGroup"/>
    </fx:define>
    <children>
        <RadioButton text="A" toggleGroup="$myToggleGroup"/>
        <RadioButton text="B" toggleGroup="$myToggleGroup"/>
        <RadioButton text="C" toggleGroup="$myToggleGroup"/>
    </children>
</VBox>

定義ブロック内の要素は、通常、後で要素の値を参照するために使用できるIDが割り当てられます。IDについては、後の項で詳しく説明します。

属性

FXMLで属性は次のいずれかを表します。

それぞれについて、以降の項で詳しく説明します。

インスタンス・プロパティ

プロパティ要素と同様に、属性を使用してクラス・インスタンスのプロパティを構成することもできます。たとえば、次のマークアップは、Click Me!というテキストが表示されるボタンを作成します。

<?import javafx.scene.control.*?>
<Button text="Click Me!"/>

プロパティ要素と同様に、プロパティ属性は型の強制をサポートしています。次のマークアップが処理される場合、x、y、widthおよびheightの値はdoubleに変換され、fill値はColorに変換されます。

<Rectangle fx:id="rectangle" x="10" y="10" width="320" height="240"
    fill="#ff0000"/>

処理時に適用されるプロパティ要素とは異なり、プロパティ属性は、それぞれの要素の終了タグに到達するまで適用されません。これは主に、要素のコンテンツが完全に処理されるまで利用できない情報に属性値が依存するケースを容易にするためです(たとえば、すべてのタブが追加されるまで設定できないTabPaneコントロールの選択された索引など)。

FXMLのプロパティ属性とプロパティ要素間のもう1つの重要な相違点は、属性が、その機能を拡張する多数の解決演算子をサポートしていることです。サポートされている演算子とその説明を次に示します。

場所解決

文字列として、XML属性は、URLなどの型指定された場所情報をネイティブに表すことができません。ただし、マークアップでこのような場所(たとえば、イメージ・リソースのソースなど)を指定することが必要な場合がよくあります。場所解決演算子(属性値への@接頭辞で表される)は、属性値を単純な文字列ではなく、現在のファイルを基準にした相対的な場所として処理する必要があることを指定するために使用されます。

たとえば、次のマークアップでは、ImageViewが作成され、現在のFXMLファイルに対する相対パスにあると想定されるmy_image.pngからイメージ・データが移入されます。

<ImageView>
    <image>
        <Image url="@my_image.png"/>
    </image>
</ImageView>

Imageは不変オブジェクトであるため、これを作成するにはビルダーが必要です。または、ImagevalueOf(URL)ファクトリ・メソッドを定義する場合は、イメージ・ビューを次のように移入できます。

<ImageView image="@my_image.png"/>

image属性の値はFXMLローダーによってURLに変換され、次にvalueOf()メソッドを使用してImageに強制変換されます。

URL内の空白値はエンコードする必要があり、たとえば、My Image.pngという名前のファイルを参照するには、FXMLドキュメントに次を含める必要があります。

<Image url="@My%20Image.png"/>

次のようにはなりません。

<Image url="@My Image.png"/>

リソース解決

FXMLでは、ローカリゼーションの目的でロード時にリソース置換を実行できます。java.util.ResourceBundleのインスタンスが提供された場合、FXMLローダーはリソース名のインスタンスをロケール固有の値に置き換えます。リソース名は、次に示すように、%接頭辞によって識別されます。

<Label text="%myText"/>

次のように定義されたリソース・バンドルがローダーに指定されるとします。

myText = This is the text!

この場合、FXMLローダーの出力は、This is the text!というテキストを含むLabelインスタンスになります。

変数解決

FXMLドキュメントでは、名前付き要素およびスクリプト変数が一意に識別される変数ネームスペースが定義されます。変数解決演算子を使用すると、コール元は、対応するセッター・メソッドが呼び出される前に、属性値を名前付きオブジェクトのインスタンスに置き換えることができます。変数参照は$接頭辞によって識別されます(次を参照)。

<fx:define>
    <ToggleGroup fx:id="myToggleGroup"/>
</fx:define>
...
<RadioButton text="A" toggleGroup="$myToggleGroup"/>
<RadioButton text="B" toggleGroup="$myToggleGroup"/>
<RadioButton text="C" toggleGroup="$myToggleGroup"/>

fx:id値を要素に割り当てると、ドキュメントのネームスペースに変数が作成されます。この変数は、後で、上に示したtoggleGroup属性や、後の項で説明するスクリプト・コードなどで、変数逆参照属性により参照できます。また、オブジェクトのタイプがidプロパティを定義する場合、この値もオブジェクトのsetId()メソッドに渡されます。

エスケープ・シーケンス

属性の値がいずれかのリソース解決接頭辞で開始する場合、先頭にバックスラッシュ文字(\)を追加することによって文字をエスケープできます。たとえば、次のマークアップは、$10.00というテキストが表示されるLabelインスタンスを作成します。

<Label text="\$10.00"/>

式バインディング

前述の属性変数は、ロード時に1回解決されます。変数値に対する以降の更新は、値の割当て先のプロパティに自動的に反映されません。多くの場合、これは十分な動作ですが、変数に対する変更がターゲット・プロパティに自動的に伝播されるようにプロパティ値を変数または式にバインドすると便利な場合がよくあります。この目的で、式バインディングを使用できます。

式バインディングも変数解決演算子で開始しますが、その後に式の値を囲む1組の中カッコが続きます。たとえば、次のマークアップは、テキスト入力のtextプロパティの値をLabelインスタンスのtextプロパティにバインドします。

<TextField fx:id="textField"/>
<Label text="${textField.text}"/>

ユーザーがテキスト入力を入力すると、ラベルのテキスト・コンテンツが自動的に更新されます。

より複雑な式もサポートされています。サポートされている定数と演算子のリストは次のとおりです。

"string"
'string'
文字列定数
true
false
ブール定数
nullnull値の定数
50.0
3e5
42
数値定数
-
(単項演算子)
数値に適用される、単項マイナス演算子
!
(単項演算子)
ブールの単項マイナス
+ -
* / %
数値バイナリ演算子
&& ||ブール・バイナリ演算子
> >=
< <=
== !=
比較のバイナリ演算子
両方の引数がComparable型である必要があります

静的プロパティ

静的プロパティを表す属性は、静的プロパティ要素と同じように処理され、同様の構文を使用します。たとえば、以前に静的プロパティ要素を説明するために示したGridPaneマークアップは、次のように書き換えることができます。

<GridPane>
    <children>
        <Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
    </children>
</TabPane>

静的プロパティ属性は、より簡潔であることに加えて、インスタンス・プロパティ属性と同じように場所、リソースおよび変数解決演算子をサポートしており、唯一の制限は、静的プロパティへの式バインディングを作成できないことです。

イベント・ハンドラ

イベント・ハンドラ属性は、ドキュメント要素に動作をアタッチする便利な方法です。setOnEvent()メソッドを定義するクラスにマークアップでイベント・ハンドラを割り当てることができます。

FXMLでは、スクリプト・イベント・ハンドラ、コントローラ・メソッド・イベント・ハンドラおよび式の3つのタイプのイベント・ハンドラ属性がサポートされています。それぞれについて次に説明します。

スクリプト・イベント・ハンドラ

スクリプト・イベント・ハンドラは、HTMLのイベント・ハンドラと同様に、イベントの起動時にスクリプト・コードを実行するイベント・ハンドラです。たとえば、ボタンのonActionイベントに関する次のスクリプトベースのハンドラは、ユーザーがボタンを押すと、JavaScriptを使用してYou clicked me!というテキストをコンソールに書き込みます。

<?language javascript?>
...

<VBox>
    <children>
        <Button text="Click Me!"
            onAction="java.lang.System.out.println('You clicked me!');"/>
    </children>
</VBox>

コード・スニペットの先頭に言語処理命令が使用されていることに注意してください。このPIは、イベント・ハンドラの実行にどのスクリプト言語を使用するかをFXMLローダーに指示します。FXMLドキュメントでインライン・スクリプトが使用される場合は、常にページ言語を指定する必要があり、ドキュメントにつき1回のみ指定できます。ただし、これは、サポートされている複数のスクリプト言語を使用して実装される場合がある外部スクリプトには当てはまりません。スクリプトについては、次の項で詳しく説明します。

コントローラ・メソッド・イベント・ハンドラ

コントローラ・メソッド・イベント・ハンドラは、ドキュメントのコントローラによって定義されるメソッドです。コントローラは、FXMLドキュメントのデシリアライズされたコンテンツに関連付けられるオブジェクトで、ドキュメントで定義されるオブジェクト(多くの場合ユーザー・インタフェース要素)の動作を調整します。

コントローラ・メソッド・イベント・ハンドラは、先頭のハッシュ記号の後にハンドラ・メソッドの名前を続けることによって指定されます。次に例を示します。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
    </children>
</VBox>

ルート要素でfx:controller属性が使用されていることに注意してください。この属性は、コントローラ・クラスをドキュメントに関連付けるために使用されます。MyControllerが次のように定義されるとします。

package com.foo;

public class MyController {
    public void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

この場合、ユーザーがボタンを押すと、handleButtonAction()がコールされ、You clicked me!というテキストがコンソールに書き込まれます。

一般に、ハンドラ・メソッドは標準イベント・ハンドラのシグネチャに準拠する必要があります。つまり、javafx.event.Eventを拡張するタイプの単一の引数を取り、(C#におけるイベント委譲と同様に) voidを返す必要があります。イベント引数は、多くの場合、イベントの性質に関して有用かつ重要な情報を持ちますが、オプションであり、必要に応じて省略できます。したがって、次も有効なハンドラです。

package com.foo;

public class MyController {
    public void handleButtonAction() {
        System.out.println("You clicked me!");
    }
}

コントローラについては、後の項で詳しく説明します。

式からのイベント・ハンドラ

javafx.event.EventHandlerタイプの変数を指す式を式ハンドラとして使用できます。

式ハンドラを使用した前述の例:

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="$controller.onActionHandler"/>
    </children>
</VBox>

次のようなフィールドを含むコントローラを使用:

public class MyController {
    
    @FXML
    public EventHandler onActionHandler = new EventHandler<>() { ... }

    ...
}  

バインディング式のような他の種類の式は、このコンテキストではサポートされません。

コレクションおよびプロパティのための特殊なハンドラ

コレクションおよびオブジェクト・プロパティは、setOnEvent()メソッドを使用してリスニングできません。そのため、特殊なハンドラ・メソッドを使用する必要があります。ObservableListObservableMapまたはObservableSetは、それぞれListChangeListner.ChangeMapChangeListener.ChangeまたはSetChangeListener.Changeパラメータを持つハンドラ・メソッドを指す、特殊なonChange属性を使用します。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children onChange="#handleChildrenChange"/>
</VBox>
ここで、ハンドラ・メソッドは次のようになります。
package com.foo;

import javafx.collections.ListChangeListener.Change;

public class MyController {
    public void handleChildrenChange(ListChangeListener.Change c) {
        System.out.println("Children changed!");
    }
}

同様に、プロパティ・ハンドラは、ChangeListenerの変更されたメソッドと同じパラメータを持つメソッドです。

changed(ObservableValue<? extends T> observable, T oldValue, T newValue)

親プロパティのハンドラは次のようになります。

public class MyController {
    public void handleParentChange(ObservableValue value, Parent oldValue, Parent newValue) {
        System.out.println("Parent changed!");
    }
}

便宜上、最初のパラメータをObservableValueのサブクラスにできます(例: Property)

プロパティに登録するために、特殊なon<propertyName>Change属性を使用する必要があります。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml" onParentChange="#handleParentChange"/>

コレクションおよびプロパティは、現在スクリプト・ハンドラをサポートしていません。

スクリプト

<fx:script>タグを使用すると、コール元はスクリプト・コードをインポートするか、またはFXMLファイル内にスクリプトを埋め込むことができます。任意のJVMスクリプト言語を使用できます(特にJavaScript、Groovy、Clojureなど)。多くの場合、Javaなどの静的に型指定された言語より、型指定の緩い言語の方がイベント・ハンドラをより簡潔に記述できるため、イベント・ハンドラを直接マークアップで、または関連付けられたソース・ファイルで定義するためにスクリプト・コードが使用されます。

たとえば、次のマークアップは、Button要素にアタッチされたアクション・ハンドラによって呼び出されるhandleButtonAction()という関数を定義します。

<?language javascript?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml">
    <fx:script>

    function handleButtonAction(event) {
       java.lang.System.out.println('You clicked me!');
    }
    </fx:script>

    <children>
        <Button text="Click Me!" onAction="handleButtonAction(event);"/>
    </children>
</VBox>

ボタンをクリックすると、関数を呼び出すイベント・ハンドラがトリガーされ、前述の例と同じ出力が生成されます。

スクリプト・コードは外部ファイルで定義することもできます。前述の例は、機能の違いなしにFXMLファイルとJavaScriptソース・ファイルに分割できます。

example.fxml
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox xmlns:fx="http://javafx.com/fxml">
    <fx:script source="example.js" charset="cp1252"/>

    <children>
        <Button text="Click Me!" onAction="handleButtonAction(event);"/>
    </children>
</VBox>
example.js

function handleButtonAction(event) {
   java.lang.System.out.println('You clicked me!');
}

多くのテキスト・エディタでは、JVMでサポートされている各種スクリプト言語の構文の強調表示が可能であるため、多くの場合、このようにマークアップからコードを分けることが適切です。これは、ソース・コードとマークアップを読みやすくするためにも役立ちます。

スクリプト・ブロックは、イベント・ハンドラ関数の定義に限定されません。スクリプト・コードは処理時に実行されるため、結果の出力の構造を動的に構成するために使用することもできます。簡単な例として、次のFXMLにはlabelTextという名前の変数を定義するスクリプト・ブロックが含まれています。この変数の値は、Labelインスタンスのテキスト・プロパティを移入するために使用されます。

<fx:script>
var myText = "This is the text of my label.";
</fx:script>

...

<Label text="$myText"/>

警告: JavaFX 8.0では、importClass() javascript関数はサポートされなくなりました。前述の例では完全修飾名を使用するか、nashorn互換スクリプトをロードする必要があります。

load("nashorn:mozilla_compat.js");
importClass(java.lang.System);

function handleButtonAction(event) {
   System.out.println('You clicked me!');
}

コントローラ

単純なイベント・ハンドラをスクリプトでインラインまたは外部ファイルで定義して記述することが便利な場合もありますが、多くの場合、より複雑なアプリケーション・ロジックをコンパイル済のJavaなどの強い型指定の言語で定義することが適切です。前述のように、fx:controller属性を使用すると、コール元はコントローラ・クラスをFXMLドキュメントに関連付けることができます。コントローラは、ドキュメントで定義されたオブジェクト階層の背後にコードを実装するコンパイル済クラスです。

前述のように、コントローラは多くの場合、マークアップで定義されているユーザー・インタフェース要素のイベント・ハンドラを実装するために使用されます。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
    </children>
</VBox>
package com.foo;

public class MyController {
    public void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

多くの場合、イベント・ハンドラをこの方法で宣言するだけで十分です。ただし、コントローラの動作およびコントローラが管理する要素をより詳細に制御する必要がある場合は、コントローラでinitialize()メソッドを定義することができ、このメソッドは、コントローラに関連付けられたドキュメントのコンテンツが完全にロードされたとき、コントローラの実装時に1回呼び出されます。

public void initialize();

これにより、実装クラスでコンテンツに対して必要な後処理を実行できます。また、コントローラには、ドキュメントのロードに使用されたリソースおよびドキュメント内の相対パスの解決に使用された場所(通常、ドキュメント自体の場所と同じ)へのアクセスも提供されます。

たとえば、次のコードは、前述の例で行ったようにイベント・ハンドラ属性を使用するのではなく、コードでボタンにアクション・ハンドラをアタッチするinitialize()メソッドを定義します。ボタン・インスタンス変数は、ドキュメントの読取り時にローダーによって注入されます。結果のアプリケーション動作は同じです。

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button fx:id="button" text="Click Me!"/>
    </children>
</VBox>
package com.foo;

public class MyController implements Initializable {
    public Button button;

    @Override
    public void initialize(URL location, Resources resources)
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("You clicked me!");
            }
        });
    }
}

@FXML

前述の例で、コントローラ・メンバー・フィールドおよびイベント・ハンドラ・メソッドはpublicとして宣言されているため、ローダーによって設定または呼び出すことができます。実際は、コントローラは通常コントローラを作成するFXMLローダーに対してのみ可視であるため、このことは多くの場合問題とはなりません。ただし、開発者がコントローラ・フィールドまたはハンドラ・メソッドの可視性をより制限する場合は、javafx.fxml.FXML注釈を使用できます。この注釈は、protectedまたはprivateクラス・メンバーをFXMLにアクセス可能としてマークします。

たとえば、前述の例のコントローラは次のように書き換えることができます。

package com.foo;

public class MyController {
    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}
package com.foo;

public class MyController implements Initializable {
    @FXML private Button button;

    @FXML
    protected void initialize()
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("You clicked me!");
            }
        });
    }
}

最初の例で、handleButtonAction()は、コントローラのドキュメントで定義されているマークアップで呼び出すことができるように、@FXMLがタグ付けされています。2番目の例で、ボタン・フィールドはローダーが値を設定できるように注釈付けされています。initialize()メソッドは同様に注釈付けされます。

@FXML注釈は、現在、信頼できるコードでのみ使用できます。FXMLローダーはメンバー・フィールドの設定およびメンバー・メソッドの呼出しをリフレクションに依存するため、いずれかの非public FieldsetAccessible()をコールする必要があります。setAccessible()は、セキュアなコンテキストでのみ実行できる権限付き操作です。これは将来のリリースで変更される可能性があります。

ネストされたコントローラ

<fx:include>要素を介してロードされる、ネストされたFXMLドキュメントのコントローラ・インスタンスは、含まれるコントローラのメンバー・フィールドに直接マップされます。これにより、開発者は、includeによって定義される機能(アプリケーションのメイン・ウィンドウ・コントローラによって提示されるダイアログ・ウィンドウなど)に容易にアクセスできます。たとえば、次のコードを考えてみます。

main_window_content.fxml
<VBox fx:controller="com.foo.MainController">
   <fx:define>
      <fx:include fx:id="dialog" source="dialog.fxml"/>
   </fx:define>
   ...
</VBox>
MainController.java
public class MainController extends Controller {
    @FXML private Window dialog;
    @FXML private DialogController dialogController;

    ...
}

コントローラのinitialize()メソッドがコールされると、dialogフィールドに"dialog.fxml" includeからロードされたルート要素が含まれ、dialogControllerフィールドにincludeのコントローラが含まれます。その後、たとえば、ダイアログを移入および表示するために、メイン・コントローラは含まれるコントローラ上でメソッドを呼び出すことができます。それ以外では、fx:includeで参照されるファイルのコンテンツは、main_window_content.fxmlから広がるシーン・グラフの一部になるため、fx:includeをfx:defineでラップして、両方のウィンドウのシーン・グラフを切り離す必要があることに留意してください。

FXMLLoader

FXMLLoaderクラスは、FXMLソース・ファイルを実際にロードして結果のオブジェクト・グラフを返します。たとえば、次のコードは、ロードするクラスに対する相対クラスパス上の場所からFXMLファイルをロードし、com.foo.exampleという名前のリソース・バンドルを使用してローカライズします。ルート要素のタイプはjavafx.scene.layout.Paneのサブクラスであると想定され、ドキュメントはタイプMyControllerのコントローラを定義すると想定されます。

URL location = getClass().getResource("example.fxml");
ResourceBundle resources = ResourceBundle.getBundle("com.foo.example");
FXMLLoader fxmlLoader = new FXMLLoader(location, resources);

Pane root = (Pane)fxmlLoader.load();
MyController controller = (MyController)fxmlLoader.getController();

FXMLLoader#load()操作の出力は、ドキュメントで実際に指定されているクラスを反映するインスタンス階層であり、それらのクラスを表すorg.w3c.domノードではありません。内部的に、FXMLLoaderjavax.xml.stream API (Streaming API for XML、つまりStAXとも呼ばれる)を使用してFXMLドキュメントをロードします。StAXは、非常に効率的なイベントベースのXML解析APIで、W3Cでの先行APIであるSAXに概念的に類似しています。これにより、FXMLドキュメントを中間DOM構造にロードしてから後処理するのではなく、単一のパスで処理できます。

カスタム・コンポーネント

FXMLLoadersetRoot()およびsetController()メソッドを使用すると、これらの値の作成をFXMLLoader自体に委譲するのではなく、コール元でドキュメント・ルートおよびコントローラの値をそれぞれのドキュメント・ネームスペースに注入できます。これにより、開発者はマークアップを使用して内部で実装されているが、(APIの観点からは)プログラムで実装されたコントロールと同一に見える再利用可能なコントロールを簡単に作成できます。

たとえば、次のマークアップは、TextFieldおよびButtonインスタンスを含む単純なカスタム・コントロールの構造を定義します。ルート・コンテナは、javafx.scene.layout.VBoxのインスタンスとして定義されます。

<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml">
    <TextField fx:id="textField"/>
    <Button text="Click Me" onAction="#doSomething"/>
</fx:root>

前述のように、<fx:root>タグは、以前に定義されたルート要素への参照を作成します。この要素の値を取得するには、FXMLLoadergetRoot()メソッドをコールします。load()をコールする前に、コール元でsetRoot()をコールして、この値を指定する必要があります。コール元では同様に、ドキュメントの読取り時にドキュメントのコントローラとして使用される値を設定するsetController()をコールすることによって、ドキュメントのコントローラの値を提供できます。一般的に、これらの2つのメソッドは、カスタムFXMLベースのコンポーネントを作成するときに同時に使用されます。

次の例では、CustomControlクラスがVBox(<fx:root>要素で宣言された型)を拡張し、それ自体をそのコンストラクタ内のFXMLドキュメントのルートとコントローラの両方として設定します。ドキュメントがロードされると、CustomControlの内容に前のFXMLドキュメントのコンテンツが移入されます。

package fxml;

import java.io.IOException;

import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class CustomControl extends VBox {
    @FXML private TextField textField;

    public CustomControl() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("custom_control.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    public String getText() {
        return textProperty().get();
    }

    public void setText(String value) {
        textProperty().set(value);
    }

    public StringProperty textProperty() {
        return textField.textProperty();
    }

    @FXML
    protected void doSomething() {
        System.out.println("The button was clicked!");
    }
}

コール元は、コードまたはマークアップでこのコントロールのインスタンスを他のコントロールと同様に使用できるようになりました。例:

Java
HBox hbox = new HBox();
CustomControl customControl = new CustomControl();
customControl.setText("Hello World!");
hbox.getChildren().add(customControl);
FXML
<HBox>
    <CustomControl text="Hello World!"/>
</HBox>

Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.