JavaFXアプリケーションの基本コード

JavaFXの基本コードは、AWTやSwingとはかなり異なる。どのパッケージのどのクラスを利用してアプリケーションを作るのか、まず基本的な部分を見てみよう。

JavaFXとは何か?

長く待たれていたJava 8が登場してから少し時間がたったが、この新しいバージョンには慣れただろうか。Java 8ではさまざまな新機能が追加されたが、その中でもJavaにとって大きな改革といえば「SwingからJavaFXへの移行」である。

これまでJavaでデスクトップアプリケーションを開発するとき、GUIライブラリとして使われてきたのはAWTとSwingだった。とはいえ、AWTはGUIの基礎を用意しているだけで、実際に活用されていたのはそれを土台に構築されたSwingだったと言っても過言ではない。Swingは長い間、一般的なJava GUIとして広く使われてきた。

しかしJava 8になり、新しいJavaFXが標準で搭載され、SwingからJavaFXへの移行が決まった。もちろん当面はSwingも共存するため、すぐになくなるわけではないが、いずれSwingが停止され、JavaFXに一本化されることはほぼ決まっているようだ。これは現在Java開発を扱うOracleのWebサイトにも明記されている。 http://www.oracle.com/technetwork/java/javafx/overview/faq-1446554.html#6 “Is JavaFX replacing Swing as the new client UI library for Java SE?”

では、このJavaFXとはどのようなものなのか。

長くSwingを利用してきた多くの人は、「どうせAWTとSwingを土台に改良したものだろう。少し見ればすぐ使えるようになるはずだ」と考えるかもしれない。Swingが登場したとき、内容的にはかなり新しくなっていても、イベントシステムやグラフィックスレンダリングシステムなどの基本構造はAWTをそのまま継承していたため、それほど混乱は起きなかった。もちろん新しいGUIが多く追加されたためAWTとはまったく違っていたが、それでも根本的な概念から理解し直す必要はなかった。Swingは「改革」や「革命」ではなかったのである。

しかしJavaFXは違う。これは革命である。既存のAWT/Swingの概念はほとんど通用しない。ButtonやTextFieldなど、以前のクラス名はそのまま継承されているが、それらを統合するStageのようなものは見たこともないクラスである。実はButtonやTextFieldも実際にはXMLコードで書くことがある。まずFrameクラスを継承しない。そのようなクラスがないからである。イベントリスナーによるイベント処理のようなものも、従来の感覚では使わない。paintメソッドやGraphicsでグラフィックを描くためのメソッドもクラスもない。

JavaFXは、AWT/Swingの基本的なGUIシステムとはまったく異なる、完全に別の体系である。その点をしっかり覚えておこう。

Applicationクラスの基本コード

それでは、JavaFXアプリケーションはどのように作るのか、その基本を見てみよう。前で「GUIの根本が違う」と少し脅かしたため、「どうやってプログラムを作るのか」と不安になる人もいるかもしれない。しかし、それほど心配する必要はない。

アプリケーションの基本は、Javaの基本どおり「mainメソッド」を実装したクラスを作成し、それを実行するだけである。通常のJavaクラスを書けば、JavaFXアプリケーションを作れるということである。この点については何も変わらない。

ただし、JavaFXアプリケーションのクラスは、Applicationという見慣れないクラスを継承して作る必要がある。

次のような、アプリケーションでもっとも基本的な形を覚えておこう。これがJavaFXアプリケーションの基本である。

public class クラス extends Application {
 
    public static void main(String[] args) {...}
 
    @Override
    public void start(Stage stage) throws Exception {...}
 
}

JavaFXライブラリはjavafxというパッケージに含まれている。アプリケーションは、javafx.applicationパッケージにあるApplicationというクラスを継承する。このクラスは抽象クラスであり、startというメソッドを含んでいるため、必ずこれを実装しなければならない。

このstartメソッドは、アプリケーションが開始するときの処理を書くためのものである。このメソッドには、javafx.stageパッケージのStageというクラスのインスタンスが引数として渡される。このStageは「トップレベルコンテナ」と呼ばれるもので、GUIの基本土台となるコンテナである。AWT/SwingではFrameやJFrameのようなクラスでウィンドウを作成して表示したが、JavaFXではこのStageを使ってウィンドウを構築するのが基本である。

Swingなどと異なり、JavaFXでは「GUIを統合するウィンドウ本体はJavaFX側で用意されている」。Stageを継承したクラスを用意してインスタンスを作るのではなく、アプリケーションを開始すると、最初に表示されるアプリケーションのウィンドウとしてStageインスタンスがstartに渡される。プログラマーは、ただその渡されたStageを利用してGUIを構築するだけである。

アプリケーションを作ってみよう

それでは実際に簡単なアプリケーションを作って動かしてみよう。次のようなサンプルコードを書く。

package com.devkuma.javafx;
 
import javafx.application.Application;
import javafx.stage.Stage;
 
public class App extends Application {
 
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage stage) throws Exception {
        stage.show();
    }
 
}

ここではcom.devkuma.javafxパッケージにAppという名前でクラスを作成した。必要に応じてパッケージ名やクラス名を書けばよい。

これを実行すると、何もない白いウィンドウが画面に現れる。閉じるボタンをクリックして閉じると、アプリケーションが終了する。非常に簡単な処理である。ここで行っていることを整理してみよう。

launchメソッド

まずmainメソッドでは、Applicationクラスに含まれているlaunchというメソッドを呼び出す。これは、クラスのmainメソッドに引数として渡されたString配列をそのまま引数として渡し、JavaFXアプリケーションを開始する。「JavaFXアプリケーションの開始はlaunch」と覚えておこう。

showメソッド

startメソッドでは、引数として渡されたStageインスタンスのshowメソッドを呼び出す。これは、そのStageで構築されたウィンドウを画面に表示するものである。逆にウィンドウを隠すhideというメソッドも含まれている。

Swingを使っていたプログラマーなら、ウィンドウの表示にshow/hideを使うことに抵抗があるかもしれない。これまではshow/hideではなく、setVisibleを使い続けてきたはずだからである。しかしStageにはsetVisibleがないことを覚えておこう。

Scene、Pane、構成要素

それでは、このウィンドウに簡単な構成要素を追加しよう。もっとも簡単なものとして、テキストを表示するだけの要素を追加してみる。

次のソースコードがその例である。

package com.devkuma.javafx;
 
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
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) throws Exception {
        Label label = new Label("This is JavaFX!");
        BorderPane pane = new BorderPane();
        pane.setCenter(label);
        Scene scene = new Scene(pane, 320, 240);
        stage.setScene(scene);
        stage.show();
    }
 
}

これを実行すると、ウィンドウ中央にThis is JavaFX!というテキストが表示される。

テキストの表示には、AWT/Swingと同じようにLabelというクラスが含まれている。ただし、これはAWT/SwingのLabelとは異なる。javafx.scene.controlパッケージに含まれているJavaFX専用のものである。

これでテキストを表示できるが、単純にStageへLabelを追加できるのだろうか。そうではない。StageではScenePaneを使わなければならない。

Sceneとは?

Sceneとは、Stageに含まれるが見えないコンテナである。Stageはウィンドウだが、このウィンドウに表示される内容を構築するのがSceneである。このSceneに含まれるものをStageに入れて表示する。このSceneは次のような形でインスタンスを生成する。

new Scene(Pane, , 高さ);

幅と高さは、生成するコンテナのサイズである。これをStageに入れることで、そのサイズにウィンドウが調整される。

ではPaneとは何か。構成要素の種類を含む土台となるコンテナである。SceneはPaneを1つだけ含めるようになっている。

Paneは1種類だけではなく、いくつかの種類がある。Paneの中にどのような構成要素を含むかによって、レイアウト方式が変わる。AWT/Swingで言えば「レイアウトマネージャ」のようなものである。ただし、レイアウト管理だけを行うレイアウトマネージャと違い、Paneは「コンテナにレイアウトマネージャ機能が組み込まれたもの」と考えるとよい。

まとめると、構成要素を使うウィンドウの構成は次のようになる。

  • StageにSceneが含まれている。
  • SceneにPaneが含まれている。
  • Paneに構成要素が含まれている。

これでようやく構成要素の種類を使えるようになる。少し構造は違うが、Swingなどでもコンテナを組み合わせて画面を作っていたので、それほど違和感はないはずである。

BorderPane

Paneにはいくつかの種類があると述べた。前の例ではBorderPaneというPaneを使った。これはBorderLayoutが組み込まれたコンテナだと考えるとよい。

このBorderPaneはnew BorderPane()のように引数なしでインスタンスを作る。構成要素は基本メソッドを呼び出して追加する。

中央に配置

*BorderPane*.setCenter(構成要素);

上部に配置

*BorderPane*.setTop(構成要素);

下部に配置

*BorderPane*.setBottom(構成要素);

右側に配置

*BorderPane*.setRight(構成要素);

左側に配置

*BorderPane*.setLeft(構成要素);

これで上下左右中央の5つの構成要素を配置できる。感覚的にはBorderLayoutと同じなので、すぐ分かるはずである。

FlowPane

このほかにもPaneはいろいろ用意されている。その中の1つがFlowPaneで、これはFlowLayoutのように、追加した構成要素を順番に整列して表示するものである。

new FlowPane();

このように引数なしでインスタンスを生成する。レイアウトの構造などはFlowLayoutと同じだが、含まれる構成要素の管理は少し異なる。

FlowPane内部では、含まれる構成要素の種類をListでまとめて管理している。このListインスタンスはgetChildrenというメソッドで取り出せる。このListに構成要素を追加して表示されるようにする。次を見ると、

pane.getChildren().add(label);

このようにして構成要素を追加していることが分かるだろう。少し不便に思う人も多いかもしれないが、「Listで管理する」というのはコンテナ内の構成要素を扱うための特別な方法だと考えないでほしい。通常のコレクションとは違うものである。

まずはBorderPaneFlowPaneを覚えれば、構成要素の基本的な組み込みを使えるようになる。そのほかのPaneについては、また近いうちに扱うことにしよう。

package com.devkuma.javafx;
 
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
 
public class App extends Application {
 
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage stage) throws Exception {
        Label label = new Label("This is JavaFX!");
        FlowPane pane = new FlowPane();
        pane.getChildren().add(label);
        Scene scene = new Scene(pane, 320, 240);
        stage.setScene(scene);
        stage.show();
    }
 
}