JavaFX Scene BuilderによるUIの構築
前回 は、NetBeansの次期バージョンである「NetBeans 7.2」と、Oracleが開発中の「JavaFX Scene Builder」( 以下、Scene Builder)を使ってJavaFXアプリケーションを作成する方法を紹介しました。今回は、自動生成されたソースコードの中身を見ながら、JavaFXアプリケーションの基本的な構造を解説します。
NetBeansで「JavaFX FXMLアプリケーション」形式のプロジェクトを作成した場合、初期状態のSample.fxmlをScene Builderで開くとUIは図1 のようになっています。
図1 JavaFX Scene BuilderによるUIの作成
左下のHierarchyパネルを見るとわかるように、AncherPaneの上にButtonとLabelのノード(AWT/Swingのコンポーネントにあたるもの)が配置されています。AnchorPaneはコンテナの一種で、外側の縁からの距離によって子ノードを配置するレイアウトが可能です。JavaFXのコンテナはそれぞれがレイアウトマネージャの機能を持つため、レイアウトに応じて使用するコンテナを選択します。
各ノードのプロパティやレイアウトの設定は、右側の「Properties」や「Layout」パネルで編集します。たとえば、Buttonのプロパティは図2 のようになっています。fx:idにはFXML上のfx:id属性(後述)を指定します。このパネルでは、ノードの色やフォントなどの見た目を変更することもできます。
図2 Buttonのプロパティ
Buttonのレイアウトは図3 のようになっています。AnchorPaneなので、「 Constraints」のところで固定したい縁をクリックすると赤く強調され、縁からの距離が指定できるようになります。
図3 Buttonのレイアウト設定
このように、UIの見た目やレイアウトを、コードを書かなくても設定できる点がScene Builderの特徴です。その一方で、Scene BuilderはUI構築のためだけのツールなので、ボタンをクリックしたときの処理などといったコントロールを設定することはできません。コントロールの部分はJavaプログラム側で行います。ただし、ユーザのアクションをどのコントローラで処理するのかという部分は、「 Document」パネルおよび各ノードの「Events」パネルで設定することができます。
Sample.fxmlのDocumentパネルは図4 のようになっています。これは、このFXMLがjavafx.sample.SampleControllerクラスに関連付けられていることを表しています。また、ButtonのEventsパネルは図5 のようになっています。これは、ボタンがクリックした際のイベントをhandleButtonAction()メソッドで受け取ることを表します。
図4 コントローラとなるクラスの指定
図5 イベントハンドラの指定
次に、Sample.fxmlのソースコードを見てみましょう。NetBeansではファイル名を右クリックして[編集]を選択するとNetBeans上のXMLエディタで開けます(ダブルクリックするとSceneBuilderが立ち上がってしまいます) 。中身は次のようになっており、最初にimport宣言がきて、次にルートコンテナであるAnchorPaneのタグがあり、childrenタグの中に子ノードであるButtonとLabelが含まれていることがわかります。AnchorPaneにはfx:comtroller属性でコントローラのクラスが、ButtonにはonAction属性でイベントハンドラのメソッドが指定されています。
<?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"
fx:controller="javafxapplication5.SampleController">
<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>
JavaプログラムからのFXMLの利用
続いてJavaプログラムの方を確認してみましょう。まず、main()メソッドを持つJavaFXSample.javaは次のようになっています。
package javafxsample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXSample extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
JavaFXアプリケーションはApplicationクラスを継承して作成します。この仕組みはアプレットをAppletクラスを継承して作るのに似ています。アプリケーションはlaunch()メソッドで起動し、init()メソッドで初期化された上で、start()メソッドが実行されます。
FXMLを使う場合には、FXMLLoaderクラスを利用してFXMLファイルを読み込んだ上で、取得したParentオブジェクトを元にしてSceneオブジェクトを作成し、それをsetScene()メソッドを用いてStageオブジェクトに設定するという手順になります。最後にStageのshow()メソッドを呼び出せばウィンドウが表示されます。
次にコントローラのコードですが、SampleController.javaの中身は次のようになっています。
package javafxapplication5;
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 SampleController implements Initializable {
@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 rb) {
// TODO
}
}
この例からも分かるように、FXMLファイルの記述との関連付けは@FXMLアノテーションを使って行います。このとき、プロパティ名はfx:id属性の値と同じものを使用します。Sample.fxmlではLabel要素のfx:id属性に"label"を指定してあるので、SampleControllerのLabelの変数名が"label"になるわけです。同様に、イベントハンドラとなるメソッドの名前も、BUttonのonAction属性(EventsパネルのOn Actionプロパティで設定)に指定した"handleButtonAction"となります。
このアプリケーションを実行すると、最初に図6 のようにボタンが表示され、ボタンをクリックすると図7 のようにラベルにテキストが追加されることが確認できます。
図6 ボタンをクリック
図7 ラベルにテキストが表示される
WebViewを使ってWebページを表示する
今度は、Scene BuilderでFXMLを変更してみます。ためしに、Webブラウザのレンダリングエンジンの機能を持るWebViewを使ってみることにしましょう。UIはButtonとURLを入力するTextField、そしてWebViewを組み合わせて図8 のようにしました。ノードの配置は、右の「Library」パネルから使いたい部品をドラッグ&ドロップするだけです。
図8 WebViewの利用
コンテナがAnchorPaneなので、各ノードに対して縁からの距離を指定できます。例えばWebViewは、図9 のように設定すれば四辺からの距離が固定されて、ウィンドウの大きさに応じてWebViewの表示領域も自動で修正されるようになります。
図9 AnchorPaneによるレイアウトの設定
変更後のSample.fxmlは次のようになりました。AnchorPane.xxxxAnchor属性によって、縁からの距離の指定が行われていることがわかります。このファイルを手動で編集する必要はありません。
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.web.*?>
<AnchorPane id="AnchorPane"
prefHeight="200.0" prefWidth="320.0"
xmlns:fx="http://javafx.com/fxml"
fx:controller="javafxsample.SampleController">
<children>
<Button fx:id="button" layoutY="14.0"
onAction="#handleButtonAction"
prefHeight="19.0" prefWidth="74.00009999999747"
text="GO!" AnchorPane.rightAnchor="13.0" />
<TextField id="textField1" fx:id="urlField" layoutY="13.0"
prefHeight="20.0" prefWidth="212.0"
text="Please input URL..."
AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="94.0" />
<WebView id="webView1" fx:id="webView"
prefHeight="139.0" prefWidth="291.9998779296875"
AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0"
AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="47.0" />
</children>
</AnchorPane>
SampleController.javaは次のようにします。
public class SampleController implements Initializable {
@FXML
private TextField urlField;
@FXML
private WebView webView;
@FXML
private void handleButtonAction(ActionEvent event) {
String url = urlField.getText();
webView.getEngine().load(url);
}
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
TextFieldにURLを入力してButtonをクリックすると、そのURLのコンテンツをWebViewに読み込んで表示するというものです。WebViewからはgetEngine()メソッドでWebEngineオブジェクトが取得できるので、それに対してload()メソッドを実行すればWebページを読み込ませることができます。
完成したら実行してみましょう。最初に図10 のように表示され、Webページを読み込ませれば図11 のようにWebView上に内容が表示されます。
図10 URLを入力して[GO!]ボタンをクリック
図11 WebView上にWebページが表示される
Scen Builderを使えば、JavaコードやFXMLコードを記述することなく、グラフィカルな操作だけでJavaFXアプリケーションのUIを構築することができるため、コーディング作業はJavaのプログラムのみに集中することができます。まだ開発途中のツールなため機能不足な部分もありますが、AWT/Swingの頃に比べればコードの見通しが良く、UI開発の生産性の向上が見込めるのではないかと思います。