在IntelliJ IDEA中使用 JAVAFX 过程记录


这个小项目弄完了,把之前记录都整理编辑了一下,后续完善的话再追加。

准备工作

javafx 在 java11 以后被单独分开了,在这里下载 SDK教程在这里

使用命令行简单编译

idea 能直接生成 JAVAFX 项目,根据教程在项目属性里添加好 lib 依赖,代码就不报错了,但是需要添加 vm option 的参数才可以编译运行。

好的,坑来了,当你根据教程配置好 PATH_TO_FX 的环境变量,并且在命令行 echo 通过了没有问题,配置进 vm option 的时候,会告诉你 FindException: Module javafx.base not found 。一般也不会用在项目中用到命令行编译,不方便。懒得试就直接跳到 gradle 编译的部分吧。

D:\develop_env\jdk11\bin\java.exe --module-path %PATH_TO_FX% --add-modules javafx.controls,javafx.fxml --add-modules javafx.base,javafx.graphics --add-reads javafx.base=ALL-UNNAMED --add-reads javafx.graphics=ALL-UNNAMED "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2\lib\idea_rt.jar=62234:C:\Program Files\JetBrains\IntelliJ IDEA 2020.2\bin" -Dfile.encoding=UTF-8 -classpath D:\demos\aaa\out\production\aaa;D:\develop_env\javafx-11.0.2\lib\src.zip;D:\develop_env\javafx-11.0.2\lib\javafx-swt.jar;D:\develop_env\javafx-11.0.2\lib\javafx.web.jar;D:\develop_env\javafx-11.0.2\lib\javafx.base.jar;D:\develop_env\javafx-11.0.2\lib\javafx.fxml.jar;D:\develop_env\javafx-11.0.2\lib\javafx.media.jar;D:\develop_env\javafx-11.0.2\lib\javafx.swing.jar;D:\develop_env\javafx-11.0.2\lib\javafx.controls.jar;D:\develop_env\javafx-11.0.2\lib\javafx.graphics.jar sample.Main
Error occurred during initialization of boot layer
java.lang.module.FindException: Module javafx.base not found

Process finished with exit code 1

但是,当你用命令行测试的时候,没问题的啊,这就很蛋疼了,我以为是后面加的参数配错了,原来是 IDEA 里不支持这么调用环境变量。需要把路径直接写入 vm option 才可以,不能用变量,就算在 IntelliJ->File->Settings->Appearance & Behavior->Path Variables 中配一遍也不行。来 骗!来 偷袭!我们刚上手写JAVAFX的新同志,这好吗?这不好。希望官方教程耗子尾汁,好好反思。

cmd_run_fx


使用 gradle 编译

好的,跨过第一个坑,接下来用 gradle 或者 maven 工具进行编译打包,我选了 gradle,maven 太丑。

根据教程,建立一个 gradle + java 项目,添加一个 module-info.java 文件,注意需要加在 src/java 的下面,和代码包根目录平级,这里仔细点没什么问题,贴一下, module 的 name 可以自定义。

module javafx {
    requires javafx.controls;
    requires javafx.fxml;

    opens tech.sunyx.sample to javafx.fxml;
    exports tech.sunyx.sample;
}

module build.gradle

plugins {
    id 'application'
    id 'java'
    id 'org.openjfx.javafxplugin' version '0.0.8'
    id 'org.beryx.jlink' version '2.12.0'
}


version '1.0.0'

javafx {
    version = "11.0.2"
    modules = [ 'javafx.controls','javafx.fxml' ]
    mainClassName = "tech.sunyx.sample.Main"
}
// gradle 6.3+ 用这个形式写,6.3 直接写在 mainClassName
run {
    main = "$moduleName/tech.sunyx.sample.Main"
}


jlink {
    launcher {
        name = 'test.exe'
    }
}

dependencies {
    implementation 'com.dlsc.formsfx:formsfx-core:11.4.1'
}

project build.gradle

buildscript {
    repositories {
        jcenter()
    }
}

subprojects {
    apply plugin: 'java'
    sourceCompatibility = 11
    group 'sunyx.tech'

    repositories {
        jcenter()
        mavenCentral()
        flatDir { dirs 'libs' }
    }

    dependencies {
        testImplementation 'junit:junit:4.12'
    }
}

并添加 build.gradle 配置好 gradle 插件。然后建立比较正式的项目结构,把刚刚那个项目的 3 个文件拿过来放在对应目录,如图。

在这里插入图片描述

看似没有问题,run 一下又报错了。告诉我 13 行 Location is required.。其实 fxml 文件放到 Resource 文件夹之后,虽然在根目录,但是需要加个 /,很容易忽略的问题。我的做法是 resource 文件夹下按照 android 的目录格式建立了一个 layout 文件夹,然后把界面文件放这里面,这样子一目了然,下次也不会忘了根目录要加 / 又出问题了

非模块化编译

真正的项目采用了 java9 的模块化写法之后(包含 module-info.java 文件),在依赖非模块化的 jar 包时可能出现多模块重复引用同一个包的问题,具体问题参考 java 模块化说明,而且此问题没有好方法解决。参考 javafx 非模块化打包应用,改用非模块化打包。


plugins {
    id 'application'
    id 'java'
    id 'org.openjfx.javafxplugin' version '0.0.9'
    //id 'org.beryx.jlink' version '2.12.0'

    id 'org.beryx.runtime' version '1.11.4'
    id "com.github.johnrengelman.shadow" version "6.1.0"
}


version '1.0.0'

javafx {
    version = "11.0.2"
    modules = [ 'javafx.controls','javafx.fxml' ]
}

application {
    mainClassName = 'com.juno.converter.Launcher'
    applicationName = project.name
}


runtime {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        // 不显示黑框
        noConsole = true
    }
    jpackage {
        if (org.gradle.internal.os.OperatingSystem.current ().windows) {
            jpackageHome = "D:\\develop_env\\jdk-16"
            imageOptions = ['--icon', 'src/main/resources/icon/hellofx.ico']
            imageOptions += ['--vendor', 'juno']
            installerName = project.name + '_win_x64'
            //installerOptions += ['--temp',"${buildDir}/tempDir"]
            installerOptions += ['--install-dir','DtsTools/data2excel']
            // 配合 resource 文件夹中的 wxl 文件,指定安装包语言参数,--temp 选项不能打开
            installerOptions += ['--resource-dir', "src/main/resources"]
            installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-shortcut']
            //installerOptions += ['--verbose']
        }
    }

}

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.12'
    annotationProcessor 'org.projectlombok:lombok:1.18.12'
    implementation ('com.alibaba:easyexcel:2.2.6') {
        exclude group: 'org.apache.poi', module: 'poi'
        exclude group: 'org.apache.poi', module: 'poi-ooxml'
    }
    implementation 'org.apache.poi:poi:4.1.2'
    implementation 'org.apache.poi:poi-ooxml:4.1.2'
    implementation 'org.slf4j:slf4j-simple:1.7.26'
    implementation project (':pojo')
    implementation project (':core')
    implementation project (':utils')
}

这个方法的好处在于用户不需要安装 jre 环境就可以解压即用,配合 jpackage 工具(java14 中发布但有 bug,用 OPENJDK16),可以做成安装包的方式符合用户习惯。

有一个坑点在于,runtime 这个插件需要一个 Launcher 类做程序入口,类名一定要叫这个,用别的类名会发现 JAVAFX 的依赖包找不到,无法启动程序。而且 官方教程 里也没说清楚,很奇怪的设计。

shadow 这个插件之前我在 打包带源码的 fatjar 到 maven 仓库 这篇文章中也有介绍,很强大的插件,这边用来打 jar 包并生成 jre 环境和启动脚本。

jpacakge

生成可执行的 exe 只需要执行 jpackageImage 这个 task,然后在 build/jpackage 文件夹下找到即可。而 jpackage 指令生成的是把可执行文件做成安装包。

jpackage 工具使用了 wix 这个 exe 生成工具,目前 jdk14 版本的 jpackage 在非英语环境 wix 打包会报错,在 JDK16 版本会修复,所以目前编译我用的 JDK11 LTS,但是打包用了 JDK16。解决方案查看 issue,需要在 src/main/resources 资源文件夹下增加一个 zh_cn.wxl。配合 gradle 中的调用,把 CultureCodepage 定义内容传给打包工具,文件内容如下

<?xml version="1.0" encoding="utf-8"?>
<WixLocalization Culture="zh-CN" xmlns="http://schemas.microsoft.com/wix/2006/localization" Codepage="936"/>

优化

瘦身

参考这篇文章,后续还有给 jre 瘦身的余地,目前打出的安装包 57M。

通过 Inno Setup 自定义安装包

打安装包需要更多自定义内容,推荐 Inno Setup 这个项目,可以编写脚本自定义各种东西。

教程 https://www.cnblogs.com/benpaodejiandan/p/7081011.html
官网下载 https://jrsoftware.org/isdl.php
中文语言包下载 https://jrsoftware.org/files/istrans/

根据需要,修改脚本代码:

  1. 修改安装包左侧位图,其大小一般为 164x314px,在 INNO 脚本的 [Setup] 段添加如下代码:WizardImageFile=Res\WizardImage.bmp

    注:红色的标记为位图路径,可以用相对路径或绝对路径

  2. 修改安装包右上角位图,其大小一般为 55x55px,在 INNO 脚本的 [Setup] 段添加如下代码:WizardSmallImageFile=Res\WizardSmallImage.bmp

  3. 修改安装包左下角的 BeveledLabel 内容,常见有两种方式:

    • 临时性修改 在 INNO 脚本的 [Messages] 段添加如下代码:BeveledLabel = 软件汉化:XXX
    • 永久性修改 用记事本打开 INNO 目录中的 “Default.isl” 修改 “BeveledLabel = ” 后的内容:软件汉化:XXX
  4. 修改安装包欢迎标签内容,即 WelcomeLabel2。
    用记事本打开 INNO 目录中的 “Default.isl” 修改 “WelcomeLabel2 = ” 后的内容:
    WelcomeLabel2 = 正在准备安装 [name/ver]…%n%n 推荐您在继续安装前关闭所有其它应用程序。%n%n 软件汉化:XXX%n%n 电子邮件:xxx%n%n

    注:%n 在 INNO 脚本中为换行符

  5. 安装结束后动行主程序或打开文件等,只需在 INNO 脚本的 [Run] 段修改代码:

Filename: {app}\PDFUnlocker.exe; Description: 启动 PDF Unlocker; Flags: nowait postinstall shellexec;
Filename: {app}\汉化说明.txt; Description: 查看说明; Flags: nowait postinstall shellexec

运行时问题

1. JNI 的动态库路径配置

System.loadLibrary 通过jvm参数中的java.library.path配置查询路径,会加载其中的so或者dll,但发现2个问题:

  1. gradle 启动Java程序。VM option 中配置 -Djava.library.path=D:/workspace/semi-secs/gem/src/main/jinLibs 无效
  2. 打包的exe发给客户如何配置java.library.path

都是通过build.gradle文件中配置做解决

  1. gradle的run命令需要通过对应的block添加属性
    run {
        systemProperty "java.library.path", 'D:/workspace/semi-secs/gem/src/main/jinLibs'
    }
    
  2. org.beryx.runtime 插件可以,launcher中增加一条配置,{{BIN_DIR}}代表app文件夹外层,和exe文件位置相同
    launcher {
            //不显示黑框
            noConsole = true
            jvmArgs = ['-Djava.library.path={{BIN_DIR}}']
        }
    
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值