はじめに
とりあえず最小の努力で(自動生成されたコードで)、JavaFXにより窓を1枚だけ画面上に表示させるという課題を、eclipse2023-03で、これに一緒についてきたJavaSE17でやってみます。というふりをして、この記事の目的はmodule-info.javaを書く練習です。
e(fx)clipseをインストールする
まずeclipseで新しいプロジェクトを作成するときのテンプレートでJavaFX Projectが選べるようにしなければなりません。このために、eclipseに新たなソフトウェアを追加インストールします。それがe(fx)clipseです。
新たなソフトウェアはHelp>Install New Software...で出るダイヤログで選択します。しかし、作業対象をreleases/2023-03で選んでe(fx)clipseを検索すると、意図しないソフトウェアが出現します。
↓これ(Minimal JavaFX OSGi integration bundles)違います。インストールしません。
作業対象に古いeclipseのリリースのoxygenを入力すると、e(fx)clipse-IDEが出てきます。これです↓。インストールしましょう。
プロジェクトを作成する
とりあえず、面倒(?)を避けるために、module-info.javaを作成しないでプロジェクトを作ってみます。プロジェクト名はblankWindowにしておきました。まずはCreate module-info.java fileのチェックはオフにしておきます。
開くWindowの種類はBorderPaneでXMLで自動コード生成してもらいます。Application TypeはDesktop、Package Nameはapplicationのままで、LanguageをFXMLとします。
自動生成によりクラスMainのコード作られます。赤下線ばっかりのエラー満載コードです。コンパイルするのにライブラリが足りないので、このようなことになってしまいました。
ライブラリをプロジェクトに追加する
JavaFXライブラリはeclipseに同梱されていませんでしたので、どこからか取得してきます。何種類かありますが、解凍するだけで使えるGluonのライブラリを使ってみます。
https://gluonhq.com/products/javafx/
↑ここの画面の下方にダウンロードできるファイル一覧があります。
使っているCPUのアーキテクチャと同梱されているソフトウェアによって適切なものを選びます。
WindowsもしくはmacOSのSDK版をダウンロードします。
openjfx-20.0.1_windows-x64_bin-sdk.zip
openjfx-20.0.1_osx-x64_bin-sdk.zip
これを適当な場所で解凍してください。eclipseのworkspaceでも結構です。そしてそのフォルダを調べるとWindows版ではbinフォルダがあってDLLがたくさん入っていますが、MacOS版ではbinがなくlibにjarも共有ライブラリファイルも含まれています。
[Windows]
javafx-sdk-20.0.1--+--bin
+--legal
+--lib
+--src.zip
[MacOS]
javafx-sdk-20.0.1--+--legal
+--lib
+--src.zip
このjavafx-sdk-20.0.1フォルダを、どこか覚えやすい場所に移動しておきます。workspaceでもよいかと思います。
この解凍したフォルダのlibに含まれるjarをJavaFXプロジェクトから参照します。
現在のプロジェクトblankWindowを選択した状態でメニューバーのproject>propertyで、ダイヤログボックスを出しJava Build PathのLibrariesタブを選びます。そしてModulepathをワンクリックして、Add External JARsボタンで、javafx-sdk-20.0.1/libフォルダになる8つのjarを取り入れます。Apply and Closeで、ソースコードの赤い波線が消えたはずです。
module-info.javaなしで実行する
普通のJavaアプリケーションのようにRunメニューから起動すると失敗します。
Mainの親クラスのApplicationがロードできないというエラーが出力されています。
エラー: メイン・クラスapplication.Mainを検出およびロードできませんでした
原因: java.lang.NoClassDefFoundError: javafx/application/Application
Mainクラスでは親クラスのAllicationに加えて、画面のBorderPaneの仕様を記載したXMLを読み出すFXMLLoaderを使っています。このために、起動パラメータにこの2つのクラスを含むモジュールのjarを追加します。
一度起動すると、たとえ起動に失敗しても、Run>Run ConconfigurationでJavaApplicationにMainという起動設定ができているはずです。それを選び、Argumentsタブを開き、VM argumentsに--add-modules javafx.controls, javafx.fxmlという2つのモジュールを追加してください。
この状態でApplyしてRunすると、なにも表示のない空白Windowが現れます。
MacOSではUse the -XstartOnFirstThread argument when launching with SWTのチェックボックスが出ますが、必ずチェックはオフです。
このWindowのCloseをクリックすると、アプリケーションが動作停止します。
モジュール化しなければライブラリをプロジェクトに取り込み、パラメータで起動時に必要なライブラリを指定すればJavaFXが動作することが分かりました。
module-info.javaを追加する
このプロジェクトをモジュール化します。それには、srcフォルダの配下にmodule-info.javaというファイルを追加します。javaという拡張子がついているもののclassを含みませんので、単にファイルを新規に作成してください。場所はここです。
内容はとりあえず、プロジェクト名と同じくmodule名をblankWindowにします。
module blankWindow{
}
これでモジュール化されたのですが、モジュール化したとたんにまるでJavaFXライブラリを取り込む前のように、赤線エラーが復活しました。モジュール化すると、外への参照や内への参照はこのmodule-info.javaに記述する必要があります。このために、ライブラリ参照ができなくなりました。まず、エラーを解消するためライブラリを参照します。必要なライブラリモジュールは、先ほど起動パラメータで指定したjavax.controlsとjavax.fxmlです。
module blankWindow{
requires javafx.controls;
requires javafx.fxml;
}
これでエラーは消えたかと思います。
module-info.javaありで実行する
このまま実行すると例外が発生します。
Exception in Application constructor
Exception in thread "main" java.lang.reflect.InvocationTargetException
これは、fxjava.controlsのApplicationが、Mainのmainを見つけられなかった、
すなわちjavaFX側から今回作ったMainクラスが参照できていないエラーです。モジュール化すると外部Jarも見えなくなってしまいましたが、逆に外から内も隠されてしまいます。MainがJavaFXから参照できるように、Mainの含まれるパッケージapplicarionを外部に公開します。
module blankWindow{
requires javafx.controls;
requires javafx.fxml;
exports application;
}
これで再びアプリケーションが起動できるようになったはずです。module-info.javaに必要なパッケージも書いたので、Run Configurationで付加した--add-modules javafx.controls, javafx.fxmlパラメータはもはや不要になります。あってもかまいませんが冗長なので削除しておきます。これで再びアプリケーションが起動できます。
まとめ
JavaFXを導入するハウツー記事のふりをして、module-info.javaでは何のために何を書かねばならなかったのかを学習しました。module-info.javaはなくても動作するのですが、このファイルの有無でデフォルト開放状態なのかデフォルト隠蔽状態なのかの違いが出てきます。module-info.javaを使い始めるとメンドクサイのですが、デフォルトでの開放状態が妙に気味が悪くなってきます。これからボタンやできることを書き足していくと直接的間接的に利用するライブラリモジュールも増えてきます。そのたびにmodule-info.javaに参照モジュールを書き足していきましょう。