本格派エンジニアの工具箱

第37回NetBeansとJavaFX Scene Builderで作るJava GUIアプリケーション その2

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の作成
図1 JavaFX Scene BuilderによるUIの作成

左下のHierarchyパネルを見るとわかるように、AncherPaneの上にButtonとLabelのノード(AWT/Swingのコンポーネントにあたるもの)が配置されています。AnchorPaneはコンテナの一種で、外側の縁からの距離によって子ノードを配置するレイアウトが可能です。JavaFXのコンテナはそれぞれがレイアウトマネージャの機能を持つため、レイアウトに応じて使用するコンテナを選択します。

各ノードのプロパティやレイアウトの設定は、右側の「Properties」「Layout」パネルで編集します。たとえば、Buttonのプロパティは図2のようになっています。fx:idにはFXML上のfx:id属性(後述)を指定します。このパネルでは、ノードの色やフォントなどの見た目を変更することもできます。

図2 Buttonのプロパティ
図2 Buttonのプロパティ

Buttonのレイアウトは図3のようになっています。AnchorPaneなので、⁠Constraints」のところで固定したい縁をクリックすると赤く強調され、縁からの距離が指定できるようになります。

図3 Buttonのレイアウト設定
図3 Buttonのレイアウト設定

このように、UIの見た目やレイアウトを、コードを書かなくても設定できる点がScene Builderの特徴です。その一方で、Scene BuilderはUI構築のためだけのツールなので、ボタンをクリックしたときの処理などといったコントロールを設定することはできません。コントロールの部分はJavaプログラム側で行います。ただし、ユーザのアクションをどのコントローラで処理するのかという部分は、⁠Document」パネルおよび各ノードの「Events」パネルで設定することができます。

Sample.fxmlのDocumentパネルは図4のようになっています。これは、このFXMLがjavafx.sample.SampleControllerクラスに関連付けられていることを表しています。また、ButtonのEventsパネルは図5のようになっています。これは、ボタンがクリックした際のイベントをhandleButtonAction()メソッドで受け取ることを表します。

図4 コントローラとなるクラスの指定
図4 コントローラとなるクラスの指定
図5 イベントハンドラの指定
図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 ボタンをクリック
図6 ボタンをクリック
図7 ラベルにテキストが表示される
図7 ラベルにテキストが表示される

WebViewを使ってWebページを表示する

今度は、Scene BuilderでFXMLを変更してみます。ためしに、Webブラウザのレンダリングエンジンの機能を持るWebViewを使ってみることにしましょう。UIはButtonとURLを入力するTextField、そしてWebViewを組み合わせて図8のようにしました。ノードの配置は、右の「Library」パネルから使いたい部品をドラッグ&ドロップするだけです。

図8 WebViewの利用
図8 WebViewの利用

コンテナがAnchorPaneなので、各ノードに対して縁からの距離を指定できます。例えばWebViewは、図9のように設定すれば四辺からの距離が固定されて、ウィンドウの大きさに応じてWebViewの表示領域も自動で修正されるようになります。

図9 AnchorPaneによるレイアウトの設定
図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!]ボタンをクリック
図10 URLを入力して[GO!]ボタンをクリック
図11 WebView上にWebページが表示される
図11 WebView上にWebページが表示される

Scen Builderを使えば、JavaコードやFXMLコードを記述することなく、グラフィカルな操作だけでJavaFXアプリケーションのUIを構築することができるため、コーディング作業はJavaのプログラムのみに集中することができます。まだ開発途中のツールなため機能不足な部分もありますが、AWT/Swingの頃に比べればコードの見通しが良く、UI開発の生産性の向上が見込めるのではないかと思います。

おすすめ記事

記事・ニュース一覧