JavaFXの環境づくりと入門のため、「IntelliJ IDEAとGradleでのJavaFXアプリケーション開発 〜環境構築からサンプルコードまで〜」をHello Worldの経典として写経してみたら意外に躓きました。
Hello Worldを表示させるまでの道のり
JavaFXを使ってHello Worldを表示させるまでの試行錯誤を書きます。
なお、Gradleを使ったのは単に好き(私には相性・印象が良い)だからです。Java 11を使ったのはJava 8(歴史のターニングポイント)の次のLTSでJava 8までしか知らない私には変化が少なく入りやすいだろうと思ったからです。JavaFX 11を使ったのはJavaFX 13がダウンロードできず(2020-4-10時点)、最新版はJava FX 16だからJava 11と相性の良さそうLTSを使いたかったからです。
JavaFX 11 SDKをダウンロード
JetBrainさんにJavaFXのプロジェクトを作る方法が書いてあったので従います。
JDKFX 11 SDKをGluonからダウンロードして展開します。この時私がダウンロードしたSDKは、openjfx-11.0.2_linux-x64_bin-sdk.zipです。
JavaFXプラグインは既に有効だったので何もしていません。
JetBrainさんのチュートリアルに従うのはここまでです。私はGradleを使いたいので。
IntelliJでGradleプロジェクトを新規作成する
Project SDKはJava 11を選びます。プロジェクト名はjavafx05としました。
念の為Project SDKを確認すると以下のような感じです。
経典に従いソースコードを作成
build.gradleを以下のように追記(編集)しました。
plugins {
id 'java'
id 'application' // 追加
id 'org.openjfx.javafxplugin' version '0.0.8' // 追加
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
runtimeOnly "org.openjfx:javafx-base:$javafx.version:linux" // 追加
runtimeOnly "org.openjfx:javafx-controls:$javafx.version:linux" // 追加
runtimeOnly "org.openjfx:javafx-fxml:$javafx.version:linux" // 追加
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:linux" // 追加
}
test {
useJUnitPlatform()
}
// --------------------
// 以下追加
// --------------------
javafx {
version = "11"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
mainClassName = 'org.example.Launcher'
jar {
manifest {
attributes 'Main-Class': 'org.example.Launcher'
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
$ gradle build
Starting a Gradle Daemon (subsequent builds will be faster)
Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.8.3/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 8s
6 actionable tasks: 6 executed
新規作成直後、すでにGradleで4つのワーニングが有りますが、Hello Worldを表示させるための障害にはならないので気にしないことにします。
Cannot resolve symbol 'Exception'を解消する
gradle buildは問題ないのですが、IntelliJでは不可解な状態になっていました。Exception等極基本的な予約語が認識されないようです。この状態ではIntelliJからRunができません。
原因は不明ですが、Project SDKを選び直すと解消しました。IntelliJとは別にSDKMAN!でインストールしておいたJava 11ならエラーになりませんでした。
SDKとエラーの関係は以下です。
- エラー:JetBrains/Toolbox/apps/IDEA-U/ch-0/203.7717.56/jbr(備え付けのJava 11)
- エラーにならない:/home/devel/.sdkman/candidates/java/current(IntelliJとは別にSDKMAN!でインストールしておいたJava 11)
- エラーにならない:jdks/adopt-openjdk-13.0.2(Java 13なので使わない)
JetBrainのページにはVMオプションでSDKへのパスを設定すべし、と書いてありましたが私の場合は設定しなくてもRunできました。
--module-path /path/to/javafx/sdk --add-modules javafx.controls,javafx.fxml
ついでにモジュール化
勉強ついでにモジュール化するため、以下のようなファイルを追加しました。
module javamoduleexample {
requires transitive javafx.graphics;
requires transitive javafx.controls;
requires transitive javafx.fxml;
// FXMLLoaderがリフレクションを使うためにopensする必要がある
opens org.example;
exports org.example;
}
実行できなくなった問題を解消
モジュール化した途端にRun(gradle run)できなくなりました。デバッガで様子をうかがうと、scene.fxml等resources/のファイルがgetResource()で読めなくなっていました。
参考にしたページによると、Gradle と IntelliJ では、 src/main/java と src/main/resources のビルド結果が、それぞれ別のディレクトリに出力されることが原因とのこと。このページに記されている解決法を適用してたら私のところでも問題が解消しました。
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
apply plugin: 'idea' // さらに追加
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
runtimeOnly "org.openjfx:javafx-base:$javafx.version:linux"
runtimeOnly "org.openjfx:javafx-controls:$javafx.version:linux"
runtimeOnly "org.openjfx:javafx-fxml:$javafx.version:linux"
runtimeOnly "org.openjfx:javafx-graphics:$javafx.version:linux"
}
test {
useJUnitPlatform()
}
// さらに追加
sourceSets {
main {
output.resourcesDir = java.outputDir
}
}
// さらに追加
idea.module.outputDir file("org/classes")
test {
useJUnitPlatform()
}
javafx {
version = "11"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
mainClassName = 'org.example.Launcher'
jar {
manifest {
attributes 'Main-Class': 'org.example.Launcher'
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
ようやくHello World画面が表示されました。
環境
- Ubuntu 18.04
- IntelliJ Ultimate 2020.3.3
- Java 11.0.10
- JavaFX 11
- Gradle 6.8.3