PROGRAMMING
Java
- IntelliJ と Gradle で JavaFX アプリケーション(非モジュール版)を作る
- javaのコンパイルと実行
- イメージを回転する
- Exif情報を簡単に取得できるライブラリ
- 高画質で画像縮小
- JDBCによるデータベース操作
javascript
- 自作カレンダーをES6で使えるようになったクラスで置き替えてみる
- JS-Image-Resizerを使って画像を高画質で縮小
- ファイルを作成しローカルに保存
- 前月・翌月の日付を埋め込んだカレンダーを作る
- モーダルウィンドウをクラス化
- ストップウォッチを作る
- XMLHttpRequest Lebel2を試してみる
- ページャーを作る(簡易版)
- jQuery:Tableで親・子・兄弟要素を取得
- ページャーを作る
- 文字列をセパレータ文字で分割し配列で返す関数
- forループのカウンタを使用している関数の定義
- クロージャの使いどころ
- JSONPでクロスドメイン
- AjaxでJSON形式のデータを扱う
- jsファイルからjsファイルを呼び出す
- 祝休日対応のカレンダーを作る
- jQuery:モーダルウィンドウを作ってみる
CSS
PHP
SQL
IntelliJ と Gradle で JavaFX アプリケーション(非モジュール版)を作る
apache netbeans 12.0で Java8 版の JavaFX アプリを、Java11 以降の JDK で書き換えようとしたが上手くいかない。
apache netbeansのサイトの情報は古いものばかりで、とても役に立たない。
そこで、IDEを思い切ってIntelliJに替えてみた。これが非常に良い。それにネット上の情報が多い。
そこで、初めてのGradleでJavaFXアプリケーションを作ってみた。
好きなもので72歳を過ぎても、まだこんなことをやっている。(2021.02.21)
今回作ってみたアプリ...外部モジュール(QRコード用)をjarに含めている
ボタンクリック後
アプリケーション作成の主な流れ
1.新規プロジェクト作成
2.パッケージを作成
3.パッケージに対応したディレクトリをresourcesに作成する
4.パッケージにjavaクラスファイルを追加
5.resourcesにfxmlファイルcssファイルを追加
6.build.gradleをjavaFX用に編集
7.ソースファイルをいろいろ作る
8.Task build で実行可能jarを作る
9.Jlinkでカスタム JRE を作る
10.jpackageで配布用パッケージ(インストーラー)を作る
11.exewrapで実行可能jarをexe化する
12.NSISでインストーラーを作る
13.追加:もしアプリをモジュール版にするとしたら(2021.02.24)
1.新規プロジェクト作成
[ウェルカム画面]若しくは、メニューバーの[ファイル] [新規] [プロジェクト] [Gradle] [Java] [次へ]
任意のプロジェクト名[fxSample]を入れて完了。 [build.gradle]ファイルが自動で生成される。
自動生成されたbuild.gradle
plugins {
id 'java'
}
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'
}
test {
useJUnitPlatform()
}
2.パッケージを作成
プロジェクトペインで[src] [main] [java]で右クリック、[新規] [パッケージ]でパッケージ名[com.jiichan]を入力Enter
3.パッケージに対応したディレクトリをresourcesに作成する
・[src] [main] [resources]で右クリック、
・[新規] [ディレクトリ]でディレクトリ名[com]を入力Enter、
・[src] [main] [resources] [com]で右クリック、
・[新規] [ディレクトリ]でディレクトリ名[jiichan]を入力Enter
プロジェクトペインではフォルダ名が[com.jiichan]となっているが、実際は[com] [jiichan]の二つのフォルダが出来ている。
4.パッケージにjavaクラスファイルを追加
[com.jiichan]で右クリック[新規] [javaクラス]でクラス名[Launcher]を入力しEnter
[com.jiichan]で右クリック[新規] [javaクラス]でクラス名[MyApplication]を入力しEnter
5.resourcesにfxmlファイルcssファイルを追加
ディレクトリ[com.jiichan]で右クリック[新規] [ファイル]でファイル名を拡張子付き[scene.fxml]で入力しEnter
ディレクトリ[com.jiichan]で右クリック[新規] [ファイル]でファイル名を拡張子付き[style.css]で入力しEnter
6.build.gradleをjavaFX用に編集
既定で作られたbuild.gradleを次のように編集。
これらの編集が無いと、
「JavaFXランタイムが構成されていません。JavaFXが組み込まれているJDKを使用するか、JavaFXライブラリをクラスパスに追加」
と警告が表示される
編集後のbuild.gradle
plugins {
// gradleでアプリケーションを実行できる(javaプラグインも使える)
id 'application'
// gradleでjavaFXが使えるように
id 'org.openjfx.javafxplugin' version '0.0.9'
// Fatjar方法1: shadowを使って実行可能jarに依存モジュール等を含める
// id 'com.github.johnrengelman.shadow' version '6.1.0'
}
application {
// plugins{application}のメインクラス
mainClassName = 'com.jiichan.Launcher'
}
// plugins{org.openjfx.javafxplugin}で使用するバージョンと依存モジュール
javafx {
version = '15.0.1'
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
// 実行可能jarのファイル名に付加されるバージョン
version '1.0'
// ダウンロードするモジュール等の所在地
repositories {
mavenCentral()
}
// 依存するモジュール等(distributionsフォルダの配布用Zipのlibフォルダにも入る)
// javaFXはpluginで使えるようになっているので記載しない
dependencies {
// QRコード生成(1次元・2次元コード画像処理ライブラリ)
implementation 'com.google.zxing:core:3.4.1'
implementation 'com.google.zxing:javase:3.4.1'
}
// 実行可能jarを作成する
jar {
// Jar内に作成されるMANIFEST.MFのMainクラスを指定(これで実行可能jarとなる)
manifest {
attributes 'Main-Class': 'com.jiichan.Launcher'
}
// FatJar方法2:jarに含める依存ライブラリ(dependenciesで指定したモジュール等)
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
実行可能jarに依存モジュール等を含める方法は、どうやら2通りあるらしい。
方法1:plugin(shadow)を使う方法
plugins {
..........
..........
id 'com.github.johnrengelman.shadow' version '6.1.0'
}
このplugin(shadow)を使う方法では、build/distributionsフォルダやbuild/libsフォルダには2組のjarができる。 allやshadowなどの名称がついたもので、どうもスッキリしない。
方法2:jarタスクを使う方法
jar {
..........
..........
from {
configurations.runtimeClasspath.collect {it.isDirectory() ? it : zipTree(it)}
}
}
この二つ目の方法ではruntimeClasspathを使うべきなのか、compileClasspathを使うべきなのかよく分からない。
色々なサイトを見るとどちらも使われていて、それぞれの意味が分からない。
runtimeClasspathの方が多いようなのでこちらを使うことした。
7.ソースファイルをいろいろ作る
アプリの出発点がLauncherクラスで、このLauncherクラスのmainメソッドが、 FXアプリであるMyApplicationクラスのmainメソッドを呼んでいる。
Launcher.java
package com.jiichan;
public class Launcher {
public static void main(String[] args){
MyApplication.main(args);
}
}
MyApplication.java
package com.jiichan;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.io.IOException;
public class MyApplication extends Application {
@Override
public void start(Stage stage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("scene.fxml"));
Scene scene = new Scene(root);
stage.setTitle("JavaFX and Gradle");
Image icon = new Image(getClass().getResourceAsStream("kobuta.png"));
stage.getIcons().add(icon);
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
FXMLController.java
package com.jiichan;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import java.awt.image.BufferedImage;
import java.util.Hashtable;
import java.util.Objects;
public class FXMLController {
@FXML
private Label label;
@FXML
private Button qrButton;
@FXML
private ImageView imgView;
@FXML
private TextField urlStr;
@FXML
private void initialize() {
}
@FXML
private void onButtonClick() {
String contents = urlStr.getText();
BarcodeFormat format = BarcodeFormat.QR_CODE;
int width = 160;
int height = 160;
Hashtable hints = new Hashtable<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
QRCodeWriter writer = new QRCodeWriter();
BitMatrix bitMatrix = null;
try {
bitMatrix = writer.encode(contents, format, width, height, hints);
} catch (WriterException e) {
e.printStackTrace();
}
BufferedImage bfimg = MatrixToImageWriter.toBufferedImage(Objects.requireNonNull(bitMatrix));
// javaFX(imageView)で表示できるように変換
WritableImage wrimg = new WritableImage(bfimg.getWidth(), bfimg.getHeight());
PixelWriter pw = wrimg.getPixelWriter();
for (int x = 0; x < bfimg.getWidth(); x++) {
for (int y = 0; y < bfimg.getHeight(); y++) {
pw.setArgb(x, y, bfimg.getRGB(x, y));
}
}
imgView.setImage(wrimg);
this.label.setText("QRコードが生成されました");
}
}
scene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<AnchorPane prefHeight="400.0" prefWidth="600.0"
stylesheets="@styles.css"
xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.jiichan.FXMLController">
<Label fx:id="label" layoutX="208.0" layoutY="25.0" text="QR code">
<font><Font size="24.0" /></font></Label>
<Button fx:id="qrButton" layoutX="208.0" layoutY="301.0"
mnemonicParsing="false"
onAction="#onButtonClick"
prefHeight="35.0" prefWidth="183.0" text="QRコード生成" />
<TextField fx:id="urlStr" layoutX="163.0" layoutY="232.0"
prefHeight="26.0" prefWidth="272.0"
text="https://jiichan.com/">
<font><Font size="17.0" /></font></TextField>
<ImageView fx:id="imgView" fitHeight="150.0" fitWidth="150.0"
layoutX="199.0" layoutY="60.0"
pickOnBounds="true" preserveRatio="true" />
</AnchorPane>
styles.css
.label {
-fx-text-fill: blue;
}
8.Task build で実行可能jarを作る
IntelliJ の右端、縦長のGraidleツールバーでウィンドウを開き、
[Tasks][build][build]をクリックすると、
暫くして左側のプロジェクトウィンドウにbuildフォルダが作られ、
その中のlibsフォルダに実行可能jarができる。
build->distributions フォルダ内の fxSample-1.0.zip の中身
binフォルダ
・fxSample(拡張子無しのファイル)
・fxSample.bat
libフォルダ
・core-3.4.1.jar(外部依存ライブラリ)
・fxSample-1.0.jar (build->libs フォルダのものと同じ 10.3MB)
・jai-imageio-core-1.4.0.jar(外部依存ライブラリ)
・javafx-base-15.0.1.jar
・javafx-base-15.0.1-win.jar
・javafx-controls-15.0.1.jar
・javafx-controls-15.0.1-win.jar
・javafx-fxml-15.0.1-win.jar
・javafx-graphics-15.0.1.jar
・javafx-graphics-15.0.1-win.jar
・javase-3.4.1.jar(外部依存ライブラリ)
・jcommander-1.78.jar(外部依存ライブラリ)
bin フォルダには fxSample.bat があり、クリックするとアプリケーションが立ち上がる。
この fxSample.bat の中を覗いてみると JAVA_HOME の java.exe で起動しているようだ。
ということは、javaFXが含まれるこの zip だけを配布しても、
そのパソコンにJREがインストールされていないと立ち上がらないことになる。
カスタムJRE(javaFXはzipに含まれているので不要) を一緒に配布する必要がある。
どうやら、このzipは
JREが入っているPCに配布するものらしい。
念のためfxSample-1.0.jarの中を覗いたらjavaFX?らしきファイルがわんさか入っていた。
結論1:実行可能jarにはjavaFXのモジュールは
含めないようだ。
buid.gradleのdependencies{}で、javaFXを含めて実行可能jarを作っても、サイズが膨らむだけで
呼ばれないのでは無駄なことになる。ウ~ン分からない。
build時にはプラグイン org.openjfx.javafxplugin によってjavaFXが使えるようになっているらしい。
結論2:javaFXはJREにだけ含める。そもそもjavaFXはJRE(ランタイム)だということか。
9.Jlinkでカスタム JRE を作る
プラグイン org.beryx.jlink を使えば graidle 上でJlink を利用できるようだが、
どうもモジュールにしなければいけないらしい。
そこで、IntelliJ のターミナル上で Jlink を使いカスタムJREを作る。
確認の意味でjavaFXが含まれないjava.base・java.desktop だけのモジュールのカスタムJRE(jre-0)と、
javafx.controls,javafx.fxmlの入ったカスタムJRE(jre-15.0.1)を作ってみた。
javaFX無し
> jlink --compress=2 --module-path c:\Java\jdk-15.0.1\jmods --add-modules java.base,java.desktop --output jre-0
javaFX有り
> jlink --compress=2 --module-path c:\Java\jdk-15.0.1\jmods;c:\Java\javafx-jmods-15.0.1 --add-modules java.base,javafx.controls,javafx.fxml --output jre-15.0.1
jdk-15.0.1をシステムPATHから外し、このカスタムJREで順次実行してみた。
> jre-0\bin\java -jar buid\libs\fxSample-1.0.jar
やはりというか当然というか、カスタムJRE(jre-0)でfxSample-1.0.jarを実行してみるとエラー(Exception in Application start method
)がでる。
良くは分からないがMyApplication.javaでscene.fxmlを読み込むところの行を示しているようだ。
次にjavaFXを含んだカスタムJRE(jre-15.0.1)でjarを実行してみた。
> jre-15.0.1\bin\java -jar build\libs\fxSample-1.0.jar
するとバッチリ立ち上がった。
10.jpackageで配布用パッケージ(インストーラー)を作る
openJDK14 から使えるパッケージツール jpackage は、そのサイトの説明によると何時また JDK から削除されるか分からないようだ。
javaアプリの起動をexeで起動できるようにラップする exewrap を使っているサイトもよく見かける。
jpackageが削除されたらそれはその時に考えるとして、取り敢えず本家で準備しているjpackageを使ってみることにした。
jpackageのオプション
オプション | 内容 |
---|---|
–type | インストーラーの形式(exe、msiなど無指定はexe) |
–name | アプリケーション名(無指定はjar名) |
–input | 実行可能jarがあるパス |
-dest | インストーラーの出力パス |
–main-jar | 実行可能jarを指定(fxSample-1.0.jar) |
–runtime-image | jlinkで作成した配布用JREがあるパス |
–vendor | 配布元の開発者等 |
–win-shortcut | デスクトップにショートカットを作成する |
-win-menu | システム・メニューに追加 |
--icon | iconファイルのパス"src\main\resources\com\jiichan\icon.ico" |
コマンドプロンプトで実行してみる
> jpackage --type msi --name fxSample --input build\libs --dest build\installer --main-jar fxSample-1.0.jar --runtime-image jreAll --vendor jiichan --win-shortcut
作られている時間は多少長かったが、最初はすんなりとmsiが出来た。PCへのインストールも問題なかった。
ショートカットなどのオプションを増やしていくと、javaのioエラーがでて進まなくなってしまった。
オプションを減らして、成功したときの状態にしても全く作ることが出来なくなってしまった。
かなり時間をかけてネットを調べたがjpackage自体が余り検索に引っかからない。
やむを得ず断念し、 exewrap を使うことにした。
11.exewrapで実行可能jarをexe化する
初めからこの exewrap を使うべきだった。
使い方も実に簡単で、よく更新もされている。海外の物とは違い何か安心感もあった。
使い方は、実行可能jarと同じフォルダにexewrap.exeをコピーし、
コマンドプロンプトで次のようにjarを指定して実行すればよい。
次の例はjavaFX、つまりウィンドウアプリケーションなので-gオプション、
アイコンも指定するので-iオプションを付加している。
> exewrap -g -i kobuta.ico fxSample-1.0.jar
素晴らしい。
12.NSISでインストーラーを作る
exewrap が使えるのでインストーラーは必要ないかと思ったが
もしもの時を考えて、インストーラーの作り方も勉強しておくことにした。
インストーラーは NSIS にした。
設定はスクリプトをnsiという拡張子のファイルに文字コードはShift-JISで書くらしい。
install.nsi
# Modern UIをインクルードする
!include MUI2.nsh
#基本データ
!define NAME "fxSample"
!define VERSION "1.0"
!define PACKAGE "${NAME}-${VERSION}"
# アプリケーション名
Name "${PACKAGE}"
# 作成するインストーラー名
OutFile "${PACKAGE}_Setup.exe"
# インストール先のディレクトリ
InstallDir "$PROGRAMFILES\${PACKAGE}"
# インストーラーページ
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
# アンインストーラ ページ
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH
# 日本語UI
!insertmacro MUI_languageUAGE "Japanese"
# インストールを中断するときに警告を出す
!define MUI_ABORTWARNING
# デフォルトセクション
Section
# 出力先を指定
SetOutPath "$INSTDIR"
# インストーラーに組み込むファイル群
File "C:\NSIS\${NAME}\${PACKAGE}.exe"
File /r "C:\NSIS\${NAME}\jre-15.0.1"
# アンインストーラを出力
WriteUninstaller "$INSTDIR\Uninstall.exe"
# スタートメニューにショートカットを登録
CreateShortcut "$SMPROGRAMS\${PACKAGE}\${PACKAGE}.lnk" "$INSTDIR\${PACKAGE}.exe" ""
# デスクトップにショートカットを作成
CreateShortcut "$DESKTOP\${PACKAGE}.lnk" "$INSTDIR\${PACKAGE}.exe" ""
# レジストリに登録
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PACKAGE}" "DisplayName" "${PACKAGE}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PACKAGE}" "UninstallString" '"$INSTDIR\Uninstall.exe"'
SectionEnd
# アンインストーラ
Section "Uninstall"
# アンインストーラを削除
Delete "$INSTDIR\Uninstall.exe"
# ファイルを削除
Delete "$INSTDIR\${PACKAGE}.exe"
# ディレクトリを削除
RMDir /r "$INSTDIR"
# スタートメニューから削除
Delete "$SMPROGRAMS\${PACKAGE}\${PACKAGE}.lnk"
Delete "$DESKTOP\${PACKAGE}.lnk"
RMDir "$SMPROGRAMS\${PACKAGE}"
# レジストリ キーを削除
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PACKAGE}"
SectionEnd
このnsiスクリプトをNSISアプリ[Compile NSI scripts]で読込ませるとインストーラー(exe)ができる。
これでインストーラーまで出来た。
長い道のりだった。しかし、世の中には頭の良い人がいっぱいいるものだ。凄い。
13.追加:もしアプリをモジュール版にするとしたら
そもそもモジュールにするというのは、アクセス権の強化のためらしい。
クラスがpublicでも、そのパッケージにはアクセスさせたくないとか。
爺ちゃんレベルだと利点が良く分からない。
ところで、もし、このアプリをモジュール版にするとしたら
まず、モジュール定義ファイルをsrc/main/java/に配置する。
これでjavaフォルダのパッケージ(com.jiichan)がモジュールになるらしい。
module-info.java
module com.jiichan {
// 依存するモジュール
requires 依存しているモジュール名
// 他のモジュールからのアクセスを許可するパッケージ
exports アクセスを許可するパッケージ名
// モジュールを限定してアクセスを許可するパッケージ
exports 許可するパッケージ名 to アクセスできるモジュール名, ...
// 実行時にのみパッケージへのアクセスを許可するパッケージ
open アクセスを許可するパッケージ名
// 実行時にのみ特定のモジュールに対してパッケージへのアクセスを許可する
opens アクセスを許可するパッケージ名 to アクセスできるモジュール名, ...
}
次に、build.gradleに一項目追加、アプリが複数モジュールの場合は更にmainModuleも追加のようだ。
build.gradle
java { // 追加
modularity.inferModulePath = true // 追加
} // 追加
application {
mainModule = 'com.jiichan' // 複数モジュールの場合追加
// plugins{application}のメインクラス
mainClassName = 'com.jiichan.Launcher'
}
ところでモジュールにする為に必要なことは少し分かったが、
依存するjarがモジュールかライブラリかはどうしたら分かるのだろう。
いちいちjarを展開してmodule-info.javaが存在するか確認するのだろうか。
その場合リポジトリからダウンロードするものだとどうなるのだろう。
分からないことだらけだ。
- 参考にさせていただいたサイト
- Gradle使い方メモ
- 多分わかりやすいGradle入門
- 【Modern Java】jpackage ツール(JEP 343) の使い方
- Gradle における Java Platform Module System (JPMS) の運用【モジュール版】
- IntelliJ IDEAでGradleを使ってJPMSプロジェクトの作成【モジュール版】
- Javaのモジュールシステム入門【モジュール版】