JavaFXアプリケーションのファイル構成（FXML, CSS, properties）

#JavaFX 3Dで赤道座標系の地球表示(2)～JavaFXアプリケーションのファイル構成～

この記事は、[JavaFX Advent Calendar 2017](https://qiita.com/advent-calendar/2017/javafx)の13日目です。12日目は、[HASUNUMA Kenji](https://qiita.com/khasunuma)さんの[JavaFX 9 WebView の WebGL 対応について](http://www.coppermine.jp/docs/notepad/2017/12/javafx-9-webview-webgl.html)です。

なお、本記事は、JavaFX Advent Calendar 2017の11日目[JavaFX 3Dで赤道座標系の地球表示(1)](https://qiita.com/boochnich/items/f31b8bc121d2a4bed6b6)の続きです。

##はじめに

これからJavaFXに取り掛かろうとすると、おそらくいろいろな壁に直面するかと思います。

- Javaのコードだけで書くか、FXMLを書くのか、CSSを書くのか、どれがいいのか
    - FXMLというXMLを書くのは大変そう、面倒そうだ
    - CSSってよくわからない
- クラス構成ってどうするのがいいのか？MVCみたいな構造はあるのか
- 文字列リソースを外部ファイルに定義して使うにはどうしたらいいのか

前回紹介したJavaFXで作成した赤道座標系での地球表示プログラムでは、Scene Builderツールを使って画面レイアウトを作成、FXMLファイルを生成し、画面に表示する色をCSSファイルに記述し、文字列リソースはプロパティファイル（英語、日本語）に記述しています。

クラス構成は、MVC（モデル・ビュー・コントローラー）を意識しており、画面レイアウトを定義するFXMLファイルがビュー、FXMLファイルと対になるJavaクラスがコントローラーです。モデルは後日追加予定です。

JavaFXに最初に触れた頃は、それまでSwingを使っていたこともあり、Javaコードで記述できることをわざわざ別な言語（FXML、CSS）を覚えて記述することが面倒で苦痛に感じていました。しかし、ちょっと使い方を理解し、またScene Builderというツールを使って画面を作成し始めると、GUIプログラムの作成が大変楽になりました。

## プログラムの構成

JavaFXでFXMLを使用するアプリケーションのプロジェクトを作成すると、大抵のビルドツール（開発環境）では、javafx.application.Applicationクラスを継承するクラス、FXMLファイル、FXMLと対になるコントローラークラスが生成されます。

赤道座標系での地球表示プログラムの最初の雛形では、次のファイル構成としました。

- com.torutk.satorbit
    - SatelliteOrbitsApp.java
    - SatelliteOrbitsView.fxml
    - SatelliteOrbitsViewController.java

NetBeans 8.2のJavaFX FXMLアプリケーションプロジェクトを新規に作成すると、FXMLとJavaソースファイルは同じディレクトリに生成されます。別なビルドツール（Maven）では、Mavenのディレクトリポリシー上FXMLファイルがJavaソースコードとは別なディレクトリに置かれます。ですが、FXMLファイルがビューとなり、コントローラーと密接な関係にあるので、コントローラーと同じ場所にある方がしっくりきます。

### JavaFXのファイル名命名について

NetBeansでは、デフォルトで付けられるFXMLファイルの名前がFXMLDocument.fxmlで、対になるコントローラークラスのソースファイル名がFXMLDocumentController.javaとなってしまいます。
そこで、MVCの関係が分かるように次の命名をしました。

| 種類                  | 命名             |
|-----------------------|------------------|
|アプリケーション名       | SatelliteOrbits |
|アプリケーションクラスのファイル名 | SatelliteOrbitsApp.java |
|ビューファイル名        | SatelliteOrbitsView.fxml |
|コントローラークラスのファイル名   | SatelliteOrbitsController.java |
|CSSファイル名           | SatelliteOrbitsView.css |
|プロパティファイル名     | SatelliteOrbitsView.properties |

アプリケーション名は、特定のクラス名ではなく、プロジェクト名に近いものです。ここで付けたアプリケーション名をベースとして各ファイルの名前を決めています。

CSSファイルや、画面に使用する文字列リソースは、Viewの関係者ということが明示されるようにビューの名前にちなんで付けています。

ファイル数が多いと面倒くさいなって思うかもしれませんが、それぞれのファイルはシンプルになります。

## ビューの作成はScene Builderを使う

ビューの作成は、FXMLをべたで編集するのではなくScene Builderをつかってポトリペタっと作成するのが楽です。

Scene BuilderでSatelliteOrbitsView.fxmlを開いた画面が次です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/9c9a5dad-00dd-d7ec-8755-8d50c5ce6cc9.png)

Scene Builerの左側には各コントロール（GUI部品）が並んだパレットと、画面レイアウトの構成（ツリー）、右側には選択したコントロールの詳細情報、真ん中にはレイアウトしている画面があります。

### Scene Builderからプレビュー画面の表示

Scene Builderの[Preview]メニュー > [Show Preview in Window]を選択すると、画面レイアウトが独立したプログラムとして実行され開きます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/792fd198-9581-65ec-7d4c-44eb3adf75b0.png)

Javaのソースコードが1行もなくてもFXMLだけでレイアウトした画面を実行し、その上にあるUI部品については操作も可能です。

### Scene BuilderにCSSとプロパティを指定

Scene Builderの[Preview]メニューには、[Scene Style Sheets]と[Internationaization]があり、それぞれCSSファイルとプロパティファイルを指定して画面レイアウトに反映することができます。

#### プロパティファイルの指定

先に示した画面は、プロパティファイルにSatelliteOrbits_ja.propertiesを指定し日本語の文字列リソースを使用しています。Scene Builderの[Preview]メニュー > [Internationalization] > [Set Resource...] から、英語のプロパティファイル SatelliteOrbits.properties を指定すると、次のように英語に表示を変えることができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/969559bb-07ca-31fe-809d-a3043df2d313.png)

プレビューで表示する画面も次のとおり、指定した英語プロパティが反映されます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/2ce9112a-19ff-f526-96ad-da051f3a14db.png)

プロパティファイルに定義した文字列リソースを使用するには、コントロールのTextプロパティには%記号を先頭に付け、プロパティファイルに定義した項目のキー名を記述します。次にScene Builder上でTextコントロールのTextプロパティに文字列リソースを指定している例を示します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/560cebd4-f77e-e695-c314-ab67b54c8f3e.png)

プロパティファイルには次の記述をしています。
- SatelliteOrbits.properties
`legend.x_axis.text = X_AXIS`

Scene Builderではなく、実際のJavaFXアプリケーションでプロパティファイルを読み込ませるためには、アプリケーションクラスで次のように指定します。

```java:SatelliteOrbitsApp.java
    public void start(Stage stage) throws Exception {
        ResourceBundle bundle = ResourceBundle.getBundle("com.torutk.satorbit.SatelliteOrbitsView");
        Parent root = FXMLLoader.load(getClass().getResource("SatelliteOrbitsView.fxml"), bundle);
```

java.util.ResourceBundleを使って、プロパティファイルを指定します（ファイルパスではなく、パッケージ名＋ファイル基底名で指定）。そして、FXMLファイルをロードするときにプロパティファイルから生成したResourceBundleを渡します。

#### CSSファイルの指定

先に示した画面は、CSSファイルにSatelliteOrbits.cssを指定し、凡例（Legend）欄のText（X軸、Y軸、Z軸）の文字色などをCSSファイルから定義しています。

Scene Builder上で、CSSで指定したいコントロールにCSSのセレクターで指定可能な定義（Style ClassかId）を定義し、CSSファイルにそのセレクターとプロパティを記述します。次にTextコントロールのIdプロパティにセレクター名x_axisを指定している例を示します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/8e33445a-e662-566d-628d-d6e5da87e5da.png)

CSSファイルには次の記述をしています。

```
#x_axis {
    -fx-fill: red;
}
```

IdプロパティをCSSのセレクターで指定するときは#で指定します。


**注意**
Scene Builderの[Preview]メニュー > [Scene Style Sheets]でCSSファイルを指定した場合は、あくまでScene Builder上だけで反映されます。Javaプログラムとして実行する場合は、別途CSSファイルを読み込む設定が必要です。設定方法を2つ紹介します。

- Javaソースコード（アプリケーションクラス）でCSSファイルを読み込み、Sceneに設定する
- Scene BuilderでトップレベルのコンテナのStylesheetsプロパティにCSSファイルを設定する

次は、2番目の方法でScene Builder上でトップレベルのコンテナ（BorderPane）のStylesheetsプロパティにCSSファイルを指定した例です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100353/1df5b50b-57f4-468a-82b1-538283316edc.png)

### Scene Builderまとめ

Scene Builderを使うと、

- FXMLファイルを手で書かなくてもよい
- Javaのコードを書かずにレイアウトが作成できる
- 修正して画面表示させての確認が素早く可能
- Java側のコード記述が少なくてすむ

などのメリットがあります。