JavaFXで流れるHello world（Java SE 8編）


# はじめに

　JavaFXは、アニメーションおよびエフェクトならびにメディアおよび3Dなどの機能を備えた現代風なGUIライブラリです。

　そのJavaFXを使った最初のGUIプログラムが、ウィンドウにラベルを貼ってその上に「Hello world」を表示するものだとしたら、それはJavaFXの魅力をあまり表現していないプログラム[^1]です。「別にSwingでもいいよね」とも思います。もっといえば、スクリプト言語から利用するTkでも同じ表示ができてしまいます。

　JavaFXライブラリの機能のうち、ボタンやラベルといったGUIコントロールおよびレイアウトは機能の一部でしかなく、それしか使わないHello worldでは勿体ないと思います。
　
　ところで、Hello worldなプログラムの目的とは、クールなデモではなく、これからそのプログラミング言語を学ぼうとする人が、初めてその言語と開発環境でソースコードを記述しビルドして実行するまでの一連の過程を手短に体験し、次のステップに進むことです。

　そのHello worldなプログラムが、複数のソースファイルに分かれていたり、コード量がスクロールしないと見えないほど多くなってしまってはHello worldなプログラムの目的が果たせません。

　そこで、JavaFXらしさを表現しつつHello worldなプログラムの目的から逸脱しないプログラムとして、今回、電光掲示板のようにHello worldメッセージが右から左へ流れていくプログラムを作ることにしました。

　ソースファイルは1つで、ソースコードの量はスクロールをしないでコード本体（import文やメソッドコメントを除いたコード部分）が見渡せることとし、30行程度を目標にします。

[^1]: こんなことを言っておきながら、自分でも http://www.torutk.com/projects/swe/wiki/JavaFX_Hello にJavaFXらしからぬHello worldプログラムをを書いてしまいました。

# プログラム開発環境について

## Java SE 8 開発環境（2021年5月記載）

Java SE 8 開発環境は、OpenJDKコミュニティによるオープンソース実装のOpenJDK 8を使用するのが一般的です。OpenJDK 8は、複数の組織・コミュニティによりOS・アーキテクチャ毎にビルドされたバイナリ（OpenJDK ディストリビューション）が提供されています。

OpenJDKのディストリビューションについては、次のWikiページにまとめています。
https://www.torutk.com/projects/swe/wiki/OpenJDK

本記事で紹介するプログラムを個人のPCの上で作成し実行する用途であれば、Oracle JDK 8をOTNライセンスの範囲で無償利用できるものと考えます。

### Oracle JDK 8

　Java SE Development Kit 8（通称Oracle JDK 8）を使用します。Oracle JDK 8は、Solaris OS、Windows OS、Linux OS、およびMac OS Xに対応しています。ただしSolaris OSはJavaFXが未対応ですので、Solaris OS以外のいずれかのOSにOracle JDK 8をインストールして使用します。各OSの詳しいバージョン・条件は次に記載されています。
https://www.oracle.com/java/technologies/javase/products-doc-jdk8-jre8-certconfig.html
　また、JavaFXを実行するためには、GUI環境（デスクトップ環境）が必要です。

　Oracle JDK 8のダウンロードサイトは次です。
http://www.oracle.com/technetwork/java/javase/downloads/index.html

　Oracle JDK 8は、Oracleと有償の契約（Java SE Desktop Subscription等）で入手、利用とするか、OTNライセンスのもとで無償で入手、利用可能です。

OTNライセンスに基づく限定された用途を日本語参考訳から引用します。
> お客様のアプリケーションの開発、テスト、プロトタイプ作成、及びデモンストレーションのみを目的として（かつ、お客様のアプリケーションが、データ処理、業務、商用又は本番利用を目的として使用されたことがない場合に限られます）

### 統合開発環境（参考）

　今回のHello worldでは、JDK 8以外にはテキストエディタがあれば作成可能です。
今後、統合開発環境があると本格的なJavaプログラミングが楽になります。代表的な統合開発環境には、[Eclipse](https://www.eclipse.org)、[IntelliJ IDEA](https://www.jetbrains.com/idea/)、[NetBeans IDE](https://ja.netbeans.org)があります[^2]。

JavaFXアプリケーション開発に使う場合、IntelliJ IDEAとNetBeans IDEは標準で対応していますが、Eclipseの場合は別途プラグイン（e(fx)clipseプラグイン）を入れる必要があります。

[^2]: 推奨順ではなく単にアルファベット順に記載しています。いずれも無償での利用可能（IntelliJ IDEAの場合はCommunity Editionが無償）です。

## JavaFX関連ドキュメント

　プログラミングではJavaFX APIのリファレンスを頻繁に参照します。このリファンレスは、Oracleからオンラインで英語版のほか、その日本語訳が公開されています。URLをWebブラウザにブックマークしておくと便利です。

JavaFX APIドキュメント（英語）
http://docs.oracle.com/javase/8/javafx/api/
JavaFX APIドキュメント（日本語）
http://docs.oracle.com/javase/jp/8/javafx/api/

　今後本格的にJavaFXのプログラミングをしていく場合は、Oracleが公開しているJavaFXのプログラミングガイド資料もブックマークしておくと便利です。

Java Platform, Standard Edtion (Java SE) 8 Client Technologies (英語)
http://docs.oracle.com/javase/8/javase-clienttechnologies.htm

Java Platform, Standard Edtion (Java SE) 8 Client Technologies (日本語)
http://docs.oracle.com/javase/jp/8/javase-clienttechnologies.htm

　今回のプログラミングで参考にした文献は本記事の末尾に記載します。

## 本記事での例示

　本記事では、Windows OS（Windows 10）上で記事更新時点（2021年5月）で最新のOracle JDK 8u291をインストールしてコマンドプロンプト上で作業をする場合を例示します。Oracle JDK 8に含まれるコマンドを実行するための環境変数PATHは設定済みとします。
 なお、画面キャプチャはWindows 7自体にキャプチャしたものをそのまま示しています。

# JavaFX Hello worldプログラミング

　いよいよJavaFXでHello worldなプログラムを作成していきます。大まかな作業の流れは次のとおりです。

* ディレクトリの作成
* ソースファイルの新規作成とコード記述
* コンパイル
* 実行

## ディレクトリの作成

　統合開発環境を使っている場合は、JavaFXの新規プロジェクトを作成します。コマンドプロンプトとエディタの場合は、作業用のディレクトリとその下にソースファイルおよびクラスファイルを格納するディレクトリを作成します[^3]。

```
C:\Users\torutk\Documents\work
  +-- hellojavafx
        +-- classes
        +-- src
```

[^3]: この作業用のディレクトリは、Hello worldなプログラム用にパッケージ宣言を省略したプログラム用としています。本格的なプログラムではパッケージに対応したディレクトリを作成する必要があります。

## ソースファイルの作成

　統合開発環境を使っている場合は、新規でJavaクラスを作成します。コマンドプロンプトとエディタの場合は上で作成したsrcディレクトリの中に、Hello world のソースファイルを新規作成します。ファイル名（MessageBoard.java）は、プログラム上のpublicなクラス名（MessageBoard）に拡張子.javaを付加した名前にする必要があります。

```
C:\Users\torutk\Documents\work
  +-- hellojavafx
        +-- classes
        +-- src
              +-- MessageBoard.java
```

## プログラミング

### JavaFXアプリケーションクラスの継承

　JavaFXでは、javafx.application.Applicationクラスを継承したクラスを作成します。そして、public void start(Stage)メソッドをオーバーライドします。まず、このコードを記述します。

```java:MessageBoard.java
import javafx.application.Application;
import javafx.stage.Stage;

public class MessageBoard extends Application {

    @Override
    public void start(Stage primaryStage) {
    }
}
```

* 注）通常、Javaではpublic static void main(String[])メソッドを定義し、プログラム実行時のエントリポイントとします。しかし、JavaFXではmainメソッドがなくても実行可能です。このあたりの仕組みにもし興味がありましたら以前に書いた次のURLのブログを参照ください。
http://d.hatena.ne.jp/torutk/20150402/p1

### 空っぽのウィンドウを表示する実装

　ウィンドウ枠だけで中が空のウィンドウを表示するには、startメソッドの引数で受け取るjavafx.stage.Stageインスタンスのshowメソッドを呼びます。Stageインスタンスはトップレベルウィンドウを表すオブジェクトです。

```java:MessageBoard.java
import javafx.application.Application;
import javafx.stage.Stage;

public class MessageBoard extends Application {

    @Override
    public void start(Stage primaryStage) {
        primaryStage.show();
    }
}
```

　ここで一度コンパイル・実行してみます。統合開発環境であれば実行ボタンを押すだけですが、コマンドプロンプトではコンパイルを実行し、続いてコンパイルされたクラスを実行します。コンパイルでは次のようにコンパイル結果のクラスファイルの出力先ディレクトリ、ソースファイルを指定します。

```
C:\Users\torutk\Documents\work\hellojavafx> javac -d classes src\MessageBoard.java

C:\Users\torutk\Documents\work\hellojavafx>
```

　コンパイルに成功すると、メッセージは何も表示せずにjavacコマンドが終了します。javacコマンドの-dオプションで指定したclassesディレクトリ下に、MessageBoard.classファイルが生成されています。

```
C:\Users\torutk\Documents\work
  +-- hellojavafx
        +-- classes
        |     +-- MessageBoard.class
        +-- src
              +-- MessageBoard.java
```

　続いてこのクラスファイルを実行します。

```
C:\Users\torutk\Documents\work\hellojavafx> java -cp classes MessageBoard

C:\Users\torutk\Documents\work\hellojavafx>
```

　実行はjavaコマンドで、-cpオプション（-classpathオプションの省略形）でクラスファイルのある基点ディレクトリを指定し、クラス名を指定します。
　プログラムが実行すると空の画面が表示されます。

![MessageBoard-1.png](https://qiita-image-store.s3.amazonaws.com/0/100353/28fe1730-8463-44bd-08c1-2e6dbfd9362b.png)

###  Hello worldのメッセージを表示する実装

　JavaFXでは、表示するコンテンツをシーングラフというツリー構造で定義します。シーングラフは、javafx.scene.Scene インスタンスとして器を用意し、そこにjavafx.scene.Nodeインスタンスのツリー構造を格納します。このツリー構造のトップはjavafx.scene.Parentインスタンス（ParentはNodeを継承）である必要があります。
　図示すると次のようになります。

```
Stage ◇-- Scene ◇-- Parent
                        ◇
                        ｜
                   +----＋-----+
                   |    ｜     |
                  Node  Node  Node
                            +--+--+
                            :  :  :
```

　今回は、シーングラフの頂点になるParentにはjavafx.scene.Groupのインスタンスを作成し、Nodeにはjavafx.scene.text.Textのインスタンスを作成します。

```java:MessageBoard.java
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class MessageBoard extends Application {

    @Override
    public void start(Stage primaryStage) {
        Text message = new Text("Hello, world. This is JavaFX.");
        Group group = new Group(message);
        group.setLayoutY(50);                        // (1)
        Scene scene = new Scene(group, 200, 100);    // (2)
        primaryStage.setScene(scene);
        primaryStage.show();
    }

}
```

1. Textの位置は下側が0となるので、デフォルトの位置（0,0）にレイアウトすると画面の上側範囲外にTextが表示されてしまいます。そこで、Y座標（縦方向）に50だけ下側に位置させています。Y座標は画面下方向が正になります。
2. Sceneには明示的に画面サイズを指定することができます。ここでは横200pixel、縦100pixelの画面サイズとしています。

　コンパイルおよび実行のコマンドは先と一緒です。実行すると次の画面が表示されます。

![MessageBoard-2.png](https://qiita-image-store.s3.amazonaws.com/0/100353/24f369a4-a208-fe3b-2ee4-c87c2be89ed0.png)

　ここまででありふれたHello worldプログラムができました。次は、このTextを右から左に流れる動作を追加します。

### メッセージを右から左へ流します

　JavaFXでは、シーングラフのツリー構造の任意のNodeに対してアニメーションを簡単に付けるTransitionクラスが用意されています。Transitionには多数の種類があり、フェードイン・アウト用のFadeTransition、塗りつぶし用のFillTransition、回転用のRotateTransition、などいろいろ用意されています。今回は、右から左へと移動させるので、平行移動を行うTranslateTransitionを使用します。

　TranslateTransitionで横方向（X軸方向）に移動させる場合は、アニメーション開始位置のX座標をFromXで、アニメーション終了位置のX座標をToXで与えます。また、その移動に費やす時間をDurationで与えます。

```
             +------------+
             | ウィンドウ |
+------------+ - - - - - -+------------+
| メッセージ |<-----------| メッセージ |
+------------+ - - - - - -+------------+
|            |            |
|            +------------+
|                         |
ToX                      FromX
```

　ここでは、FromXをウィンドウの幅である200に、ToXはウィンドウの幅だけをウィンドウ左端（X=0）から左にずらした-200に設定します。アニメーションに費やす時間を8秒とし、左端まで移動したらまた右側から流れてくるように繰り返し回数を無制限にします。

　コードにすると次になります。

```java:MessageBoard.java
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;

public class MessageBoard extends Application {

    @Override
    public void start(Stage primaryStage) {
        Text message = new Text("Hello, world. This is JavaFX.");

        TranslateTransition messageTransition
            = new TranslateTransition(Duration.seconds(8), message);  // (1)
        messageTransition.setFromX(200); // (2)
        messageTransition.setToX(-200);  // (3)
        messageTransition.setInterpolator(Interpolator.LINEAR); // (4)
        messageTransition.setCycleCount(TranslateTransition.INDEFINITE); // (5)

        Group group = new Group(message);
        group.setLayoutY(50);
        Scene scene = new Scene(group, 200, 100);
        primaryStage.setScene(scene);
        primaryStage.show();

        messageTransition.play(); // (6)
    }

}
```

1. 平行移動アニメーションを行うTranslateTransitionインスタンスを、1回のサイクルに要する時間および対象ノード（Text）を指定して生成
2. 平行移動開始位置を指定（画面右端より右側画面外にTextが位置する）
3. 平行移動終了位置を指定（画面左端より左側画面外にTextが位置する）
4. 移動は均等（等速直線運動）になるよう指定、デフォルトは徐々に加速し徐々に減速する動きです。
5. アニメーションを繰り返し実行する指定
6. アニメーションを開始

　コンパイルおよび実行のコマンドは先と一緒です。実行すると次の画面（実際の画面をアニメーションGIFでキャプチャ作成）が表示されます。

![MessageBoardDisplay-1.gif](https://qiita-image-store.s3.amazonaws.com/0/100353/1d76c9ad-5d2a-e003-1f9a-450bb7005841.gif)

### フォントの色と大きさ、画面サイズの調整

　ここまでのコードでimport文、空行、括弧のみの行を含めて32行となっています。そろそろHello worldなプログラムの限界ですので最後にちょっとだけ見栄えの追加をして終わることとします。

　フォントの色、種類と大きさを設定します。ここでは濃い紫、Serif系フォント（明朝系）で大きさを32ポイントにします。次に画面の大きさを、テキストの表示領域に合わせて動的に決定します。画面幅はテキストの幅と同じ、高さはテキストの高さの3倍とします。

```java:MessageBoard.java
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;

public class MessageBoard extends Application {

    @Override
    public void start(Stage primaryStage) {
        Text message = new Text("Hello, world. This is JavaFX.");
        message.setFill(Color.DARKMAGENTA);   // (1)
        message.setFont(Font.font("Serif", FontWeight.SEMI_BOLD, 32)); // (2)
        double messageWidth = message.getLayoutBounds().getWidth();   // (3)
        double messageHeight = message.getLayoutBounds().getHeight(); // (4)

        TranslateTransition messageTransition
            = new TranslateTransition(Duration.seconds(8), message);
        messageTransition.setFromX(messageWidth);  // (5)
        messageTransition.setToX(-messageWidth);   // (6)
        messageTransition.setInterpolator(Interpolator.LINEAR);
        messageTransition.setCycleCount(TranslateTransition.INDEFINITE);

        Group group = new Group(message);
        group.setLayoutY(messageHeight * 2);  // (7)
        Scene scene = new Scene(group, messageWidth, messageHeight * 3); // (8)
        primaryStage.setScene(scene);
        primaryStage.show();

        messageTransition.play();
    }

}
```

1. Textノードの色は塗りつぶし色（fill）で指定します。JavaFXのColorは、多数の定数定義があります。
2. フォントの種類にSerif（明朝系）を指定し、書体をやや太く、サイズを32ポイントに指定します。Windows用のJDK 8のフォント設定では、Serifを指定するとMS明朝が使われます。
3. Textがレイアウト上占める領域の大きさ（幅）を取得します。
4. Textがレイアウト上占める領域の大きさ（高さ）を取得します。
5. 平行移動開始位置をTextの幅に相当する位置（画面右端外に位置）に設定します。
6. 平行移動終了位置をTextの幅に相当する位置（画面左端外に位置）に設定します。
7. 画面の高さを8.でTextの高さの3倍に設定しているので、その際におおよそ真ん中に配置されるよう指定しています。
8. 画面の大きさはTextの大きさ（3.および4.で取得した値）に応じて決定します。

　ソースコードは全体で39行です。

　コンパイルおよび実行のコマンドは先と一緒です。実行すると次の画面（実際の画面をアニメーションGIFでキャプチャ作成）が表示されます。

![MessageBoardDisplay-2.gif](https://qiita-image-store.s3.amazonaws.com/0/100353/815a6aba-b5cc-46e5-307a-39a494b3af39.gif)

# プログラムの配布と実行

　このプログラムは現時点ではclasses以下に生成されたファイル一式を必要とし、プログラムの実行にはコマンドラインからjavaコマンドでクラスパス指定およびクラス名指定をする必要があります。いくらJavaFXが現代的なGUIであっても、プログラムを実行するのにコマンドラインからコマンドを叩くのでは前時代的過ぎます。

　そこで、今回はJavaプログラムの実行方法の中の一つ実行可能JAR形式で作成します。実行可能JAR形式は、Windows OS上であればJARファイルをダブルクリックで実行することができます。Linuxの場合はデフォルトではダブルクリックで起動しないので何らかの設定が必要です。Mac OS Xは情報がないので不明です。

## 実行可能JAR形式の作成

　JARファイルはZIP書庫の形式に所定のルールでJavaのクラスファイルや各種リソースファイル、定義ファイルを格納したものです。定義ファイルの1つにマニフェストファイルがあります。このマニフェストファイルにMain-Class定義を記述すると、実行可能JARとして扱われます。

　マニフェストファイルを作成します。ファイル名は何でも可、ここではmanifest.txtとします。

```
C:\Users\torutk\Documents\work
  +-- hellojavafx
        +-- classes
        |     +-- MessageBoard.class
        +-- src
        |     +-- MessageBoard.java
        +-- manifest.txt
```

manifest.txtを新規作成します。

```
Main-Class: MessageBoard
```

実行可能JARファイルを作成します。

```
C:\Users\torutk\Documents\work\hellojavafx> jar cmf manifest.txt hello.jar -C classes .

C:\Users\torutk\Documents\work\hellojavafx>
```

　hello.jar が生成されました。これをエクスプローラー上でダブルクリックすると、プログラムが実行されます。

　コマンドラインから実行するときは、Windowsであればhello.jarと入力すれば実行されます。Oracle JDKをインストールして有効にしたLinux（CentOS 6）でも実行できました。

```
C:\Users\torutk\Documents\work\hellojavafx> hello.jar
```

　これならば、プログラムの配布はJARファイル1つだけで済みます。実行する際もWindowsであれば他のプログラム同様ダブルクリックでいけます。OSには依存していないので、このJARファイルはLinuxやMac OSへ渡して実行することができます。

# 最後に

　JavaFXのアニメーション機能を使って流れるHello worldメッセージを表示するプログラムを記述し、コンパイル、実行、そして配布までを体験することができました。
　ソースコード量も、ソースファイル1つ、ソースコード行数は39行で、ロジックを記述しているメソッドはstartメソッド1つだけで、これは23行になります。
　
　これをきっかけにJavaFXプログラムに興味を持ってもらえたとしたら幸いです。

　JavaFX Advent Calendar 2015もまだまだ空きがあるので、このプログラムにちょっと特殊効果を追加してみました、例えばメッセージ文字にエフェクトを追加するとか、背景をグラデーションする、などの内容で参加いただけると嬉しいです。

# 参考文献

* [Oracle Javaドキュメント JavaFX: 変換、アニメーションおよび視覚効果 3 アニメーションの基本](http://docs.oracle.com/javase/jp/8/javafx/visual-effects-tutorial/basics.htm#CIHHAACG)
* [JavaFX 2ではじめるGUI開発　第12回 トランジションで簡単アニメーション](http://itpro.nikkeibp.co.jp/article/COLUMN/20130625/487483/)


### 脚注