JavaFX周りの備忘録

## 本記事の前提事項
※記事を書いている人は過激派です。

どっか行けヽ( ・∀・)ﾉ┌┛Σ (Eclipse)

三(メモ帳) ლ(^o^ლ)おいで
三(コマンドプロンプト) ლ(^o^ლ)おいで

## 前段
JavaFXと言えば、JavaでGUIアプリケーションを作るときに使われる便利なフレームワークだ。
AWT、Swingといった旧来のフレームワークよりも簡潔に、そして表現力を豊かにしたフレームワークだ。

基本的には`javafx.application.Application`クラスを継承したクラスを定義し、GUIをFXMLで定義し、`Controller`クラスを定義してイベントハンドラを割り当てたりする。

Java8の頃にはJDKにも組み込まれ、作ったjavaファイルをコンパイル→jarファイルに変換すれば、`java -jar App.jar`だとかで走らせることができた。

### そんなJavaFX君に悲劇が…
2018年頃だっただろうか、Java8からJava11への変更に伴い、JDKから追い出されてしまったのだ。
まるで幼児がニンジンやピーマンを皿の脇に追いやるようにっ…！

### じゃあどうするんだ、と
OpenJFXを使え、と。(同梱してくれても良いジャマイカ)
おかげさまで開発準備も面倒になり、Module Systemの導入によりコンパイル作業も面倒になってしまった…omg
そんな面倒ごとの備忘録として、この記事を残す。

## 環境準備

### ファイルのダウンロードとか
Java11の環境をこさえるために、[OpenJDK](jdk.java.net)と[OpenJFX](openjfx.io)をダウンロードする。
OpenJDKは解凍して適当なフォルダに入れておく。(ここでは`C:/Program Files/Java`とする)
OpenJFXも解凍し、同じフォルダに入れておく。
この時点で、フォルダ構成は下記の通り。
```
C:/Program Files/Java
+-- javafx-sdk
|   +-- bin
|   +-- legal
|   `-- lib
+-- javafx-jmods
|   `-- *.jmod
`-- jdk
    +-- bin
    +-- conf
    +-- include
    +-- jmods
    +-- legal
    `-- lib
```

### 環境変数作る
`JAVA_HOME`にJDKのパスを、`JAVAFX_HOME`にSDKのパスを貼り付ける。
ついでに、`Path`に`%HAVA_HOME%\bin`を追加しておく。
基本的なコマンドが通ればおk。

### プロジェクト用のフォルダを作る
細かい説明は省略。
```
JavaFXDemo
+-- bin
+-- dest
+-- res
|   +-- layout
|   |   `-- layout.fxml
|   `-- icon.png
+-- src
|   +-- javafxdemo
|   |   +-- icon.ico
|   |   +-- module-info.java
|   |   +-- MainApplication.java
|   |   `-- MainController.java
|   `-- MANIFEST.MF
`-- compile.bat
```
FXMLを使わないなら、`layout`とか`MainController.java`は不要。

## 作ってみる
準備した環境で動くか確認する。

### プログラムを書く

#### MainApplication.java
`MainApplication.java`には`Stage`の表示とかのプログラムを書いておく。
FXMLは`FXMLLoader`で読み込んで、`Stage#setScene(Scene scene);`で割り当てる。(ここでは省略)
```java:MainApplication.java
package com.example;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.stage.Stage;

public class MainApplication extends Application {
  @Override public void start (Stage primaryStage) {
    // (ry
    primaryStage.show();
  }

  public static void main (String[] args) {
    Application.launch(MainApplication.class, args);
  }
}
```

#### MainController.java
ここはクラスを定義するだけ。
強いて言うなら、`javafx.fxml.Initializable`を実装すると良いかも。
FXMLで指定したイベントハンドラとかは、アノテーション(`@FXML`)を使って記載する。

#### module-info.java
面倒になった諸悪の根源の1つ。
ほとんど定型なのでコピペする。
コンパイルでエラー吐いたら、`requires`とか`opens`が正しいか見る。
```java:module-info.java
module javafxdemo {
  exports com.example.javafxdemo;
  requires java.desktop;
  requires javafx.controls;
  requires javafx.fxml;
  opens com.example.javafxdemo to javafx.fxml, javafx.graphics;
}
```

### コンパイル用のbatを作る
もっと綺麗に書くべきだろうけど、分かりやすいのでコピペする。
難しいことは考えたら負け。
`C:\Program Files\Java\javafx-jmods`辺りは環境変数にしておいて良いかも。
モジュール周りは適宜変更する。
```bat:compile.bat
@echo off
setlocal
if exist bin rd /s /q bin
md bin
if exist dest rd /s /q dest
md dest
set package_name=com.example.javafxdemo
set module_name=javafxdemo
set manifest_file=src\MANIFEST.MF
set jar_file=dest\JavaFXDemo.jar
set launcher=JavaFXDemo=%module_name%/%package_name%
javac -d bin -encoding utf-8 --module %module_name% --module-path "%JAVAFX_HOME%\lib" --module-source-path src -Xlint:unchecked
xcopy /s /y res bin\%module_name%\res\
jar cvfm %jar_file% %manifest_file% -C bin\%module_name% .
jlink --add-modules %module_name% --compress=2 --launcher %launcher% --module-path "dest;%JAVA_HOME%\jmods;C:\Program Files\Java\javafx-jmods" --output dest\%module_name%
dest\%module_name%\bin\JavaFXDemo
cmd /k
endlocal
@echo on
```

### コンパイルとか
コマンドプロンプトから`compile`を実行する。
ブランクの画面が表示されればおk。
エラー吐かれたら調べて対応する。

## まとめ
ど　う　し　て　こ　う　な　っ　た

Androidのファイル操作周りが厳格になったり、安全性が重視される世の中なのは理解する。
でも、開発の簡単さをもう少し…ほんの少しで良いので…

…まぁ正直、JavaFXで作るよりもPythonとかで作った方が楽な気もする()