JavaFX FXML GUIデザイン

JavaFXでは「FXML」というシンプルな言語を使うことで、XMLを利用してGUIを簡単にデザインできる。ここではFXMLの基本的な使い方を説明する。

FXMLとは

JavaFXは、豊かなGUIを持つアプリケーションを素早く開発できることを重視して作られたGUIライブラリである。しかし、ここまで見てきた範囲では「Swingとあまり変わらない」という印象を持ったかもしれない。アクションイベントの設定などは簡単だったが、「この程度ならわざわざSwingから移行する必要はない」と感じた人も多いだろう。

実はJavaFXでは、Javaクラスを作ってJavaソースコードでGUIを構築する方式が中心というわけではない。それ以上に重視されているのが、FXMLを利用したGUIデザインである。

FXMLは、JavaFXに含まれているXMLベースのGUIマークアップ言語である。「言語」といってもXMLベースのマークアップなので、Javaソースコードを書くよりも記述はかなり簡単である。利用するコンテナやコントロールの内容をXMLとして記述するだけでGUIをデザインできる。その後はJava側でこれを読み込んで表示する短いコードを書くだけで、本格的なGUIアプリケーションを作成できる。

JavaFX Scene Builderについて

FXMLがXMLベースで記述できる利点は、単に「書きやすい」ことだけではない。XMLベースであるため解析しやすく、各種ツールによってデザインしやすいという利点もある。実際にJavaの開発元であるOracleは、FXMLに対応したGUIデザインツール「JavaFX Scene Builder」を提供している。

このツールは、マウスで部品をドラッグして配置するだけでFXMLソースコードを作成できるツールである。FXMLを利用するなら、このようなGUIデザインツールの利用は必須といってよい。まずはこうしたツールを使えるようにしておこう。

JavaFX Scene Builderのダウンロード先:

http://www.oracle.com/technetwork/java/javase/downloads/javafxscenebuilder-info-2157684.html

FXMLの基本コード

では、FXMLファイルとはどのような形になっているのだろうか。基本コードをざっと理解しておこう。

FXMLは.fxml拡張子のテキストファイルとして作成される。このソースコードの基本形をまとめると、おおよそ次のようになる。

FXMLの基本形

<?xml version="1.0"  encoding="UTF-8"?>

<?import XX ?>

<Paneクラス
   xmlns="..." 
   xmlns:fx="...">
   <!-- Paneに含めるコントロール類 -->
</Paneクラス>

<?xml ?>の後に、Javaのimport文に相当する<?import ?>タグを用意する。これにより使用するクラスをimportできる。<?import ?>タグを書かない場合、クラスはすべて完全なパッケージ名で指定しなければならないので注意しよう。

FXMLのルートタグ、つまり最上位タグはPaneクラスのタグで作成する。たとえばBorderPaneなら<BorderPane>タグを書き、その中にコントロールなどのタグを書く。

基本的にFXMLでは、Javaのクラス名をそのままタグ名として書けば多くの場合認識される。Labelなら<Label>という形である。

ルートタグにはxmlns、つまりXML名前空間の属性を記述する。また、FXML自身の名前空間属性としてxmlns:fxも用意する。この2つの属性により、このXMLコードがFXMLコードとして認識される。

実際には、単にJavaコード内でFXMLファイルをロードして使うだけなら、これらの名前空間属性はなくてもよい。書かなくてもJava内でFXMLを正しくロードして利用できる。

これらはGUI作成ツールなどを使うときに役立つが、必ずしもなくてはならないものではない。それでもFXMLの基本として書いておく習慣をつけよう。

次はFXMLの基本コード例である。

<?import XX ?>
 
<Paneクラス
    xmlns="http://javafx.com/javafx/8"
    xmlns:fx="http://javafx.com/fxml/1">
   <!-- Paneに含めるコントロール類 -->
</Paneクラス>

FXMLを使う

FXMLを使った簡単なサンプルを作ってみよう。次のコードがFXMLの例である。

<?xml version="1.0" encoding="UTF-8"?>
 
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
 
<BorderPane
    xmlns="http://javafx.com/javafx/8"
    xmlns:fx="http://javafx.com/fxml/1">
    <top>
        <Label text="This is FXML!" />
    </top>
</BorderPane>

これはLabelを1つBorderPaneに追加しただけの単純な例である。内容を説明すると次のようになる。

<? import?>タグ

最初に複数の<? import?>文が書かれている。実はこれはJavaFX Scene Builderで作成したときに自動で追加されるものである。Paneやコントロール関連のクラスのimport文をまとめて用意している。

<BorderPane>タグ

ここでは<BorderPane>というタグがあり、その中に<Label>タグが書かれている。よく見ると、直接書かれているのではなく、<Top>というタグで囲まれていることがわかる。これはBorderPane特有のもので、結合される位置を表すタグの中にコントロールのタグを書く仕組みである。用意されているのは次の5つである。

<BorderPane>の位置タグ

<Top>, <Bottom>, <Right>, <Left>, <Ceter>

<Label>タグ

<Label>タグでは、text属性に表示するテキストを指定する。コントロールのプロパティは、このように属性としてタグに書くことができる。text="OO"setText("OO")のような処理をしていると考えると理解しやすい。

このように、コントロールのプロパティを設定するsetOO(値)の処理は、そのままコントロールタグのOO=値のような形に置き換えられるものが多い。これはLabelだけでなく、コントロール全般に言えることである。

FXMLをロードする

FXMLは当然ながら、書いただけでは動作しない。Javaクラス側でこれをロードしてインスタンス化し、ウィンドウに組み込んで初めて利用できる。これは一般的にApplicationクラスのstartメソッドに処理を書く作業になる。FXML利用の流れを整理しよう。

Paneをロードする

FXMLをロードしてPaneインスタンスを生成する。これはjavafx.fxmlパッケージのFXMLLoaderクラスを使う。この中のloadクラスメソッドを呼び出すことで、FXMLファイルをロードしインスタンスを取得できる。

FXMLLoader.load(URL)

引数にはURLインスタンスを渡す。FXMLファイルがApplicationクラスと同じ場所に保存されている場合、ClassgetResourceメソッドでロードするのがよい。つまり、次のような形である。

変数 = FXMLLoader.load(getClass().getResource("xxx.fxml"));

このxxx.fxmlにロードするFXMLファイル名を指定すればよい。注意すべき点は取得されるインスタンスである。これはObjectインスタンスとしてキャストされているため、取得後に本来のPaneインスタンスへ変換して利用する。

Sceneに追加する

FXMLからPaneインスタンスを取得したら、あとは簡単である。Sceneインスタンスを生成するときにこのPaneを引数として指定し、Sceneを用意してStageに設定すればよい。

Scene 変数 = new Scene(Paneインスタンス, 幅, 高さ);

Sceneはこのようにインスタンス化できる。FXMLを利用しても、Paneインスタンスさえ得られれば後は変わらない。

次は実際の使用例である。

package com.devkuma.javafx;
     
import java.io.IOException;
 
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
 
public class App extends Application {
     
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage stage) {
            BorderPane root;
            try {
                root = (BorderPane)FXMLLoader.load(getClass().getResource("app.fxml"));
                Scene scene = new Scene(root,200,100);
                stage.setScene(scene);
                stage.show();
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
}

ここではcom.devkuma.javafx.Appクラスと同じ場所に、app.fxmlというファイル名でFXMLファイルが配置されているという前提でコードを書いている。実際に前のFXMLファイルを配置して実行してみよう。正しくウィンドウが表示されるはずだ。

スタイルシートを利用する

コントロールの細かな表示設定は、FXMLの属性として書き込むよりも、さらにわかりやすい方法がある。それが「スタイルシート」を使う方法である。JavaFXには、コントロールのプロパティをスタイルシートから読み込んで適用する機能がある。これを利用すれば、コントロールの表示をJavaコードで書かなくても簡単に構成できる。

スタイルシートの使用はとても簡単である。FXMLのPaneタグの中に、次のような形でスタイルシートを書く。

<stylesheets>
    <URL value="@スタイルシート"/>
</stylesheets>

<stylesheets>タグの中に、ロードするスタイルシートのURLを用意する。必要な数だけ書ける。<URL>タグには、valueでロードするスタイルシートのパスを指定する。@ファイル名のように書くことで、FXMLファイルと同じ場所にあるスタイルシートファイルを指定できる。

次のソースコードは、前のFXMLにスタイルシートを追加したものである。

<?xml version="1.0" encoding="UTF-8"?>
 
<?import java.net.URL ?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
 
<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <stylesheets>
        <URL value="@app.css" />
    </stylesheets>
    <top>
        <Label fx:id="label1" text="This is FXML!" />
    </top>
</BorderPane>

これでFXMLファイルと同じ場所にあるapp.cssをロードする。ここではスタイルシートを適用するため、<Label>タグにIDを指定している。

<Label fx:id="label1"... />

このようにfx:idと書くことで、そのコントロールにIDを指定できる。スタイルシートではこのIDを使って、特定のコントロールにスタイルを適用できる。

スタイルシートを書く

ではスタイルシートを書いてみよう。まずFXMLファイルと同じ場所にapp.cssというファイル名で作成する。

そしてファイル内に、FXMLで書いた<Label fx:id="label1"/>コントロールのスタイルを記述する。次は簡単な例である。

@CHARSET "utf-8";
 
Label#label1 {
    -fx-font-family:Serif;
    -fx-font-size:24pt;
}

文字コードの指定

先頭に@CHARSET "utf-8";が書かれている。基本的にスタイルシートはUTF-8でエンコードする。これは必須ではないが、書いておくのが基本だと考えよう。

コントロールのスタイル

ここではlabel1というIDを持つLabelに対してスタイルが書かれている。

Label#label1 {...}

このようにスタイル指定は「クラス名#ID名」のような形式で書く。HTMLのスタイルで「タグ名#ID」と書くのと同じ感覚である。

もちろんクラス名だけを指定すれば、そのクラスのコントロールすべてにスタイルを適用できる。たとえばLabel {...}とすれば、すべてのLabelのスタイルをまとめて設定できる。

フォント属性について

ここでは2つのスタイルが書かれている。-fx-font-family-fx-font-sizeである。これらはフォントファミリー名とフォントサイズを指定するものだ。

JavaFXのスタイルは、すべてこのように-fx-OOで始まる名前になっている。多くはHTMLで使われるスタイル名の前に-fx-を付けるだけで利用できるが、必ずしもすべてがそうではない。

たとえば背景色は-fx-background-colorだが、テキスト色は-fx-text-fillになっている。JavaFX独自の値もあるので注意しよう。