为具有预加载器的 JavaFX 应用程序创建本机包
Create a native bundle for a JavaFX application that has a preloader
我有一个使用预加载器的 JavaFX 应用程序。我想做的是将其打包为本机包(Mac 应用程序或 Windows 包含 Java JDK 副本的 exe 文件)以便用户在他们的计算机上没有正确版本的 Java 仍然可以 运行 该应用程序。我已按照 creating native bundles and for adding preloaders 的 Oracle 说明进行操作。我得到的正是您所期望的 — 运行 是我的程序的本机包。
问题是捆绑包完全忽略了我的预加载器。它只是 运行 的主程序(经过很长的加载时间)。我知道包含预加载器,因为当我单独 运行 jar 文件时,它会显示出来。
有没有人成功地将 JavaFX 应用程序与预加载器捆绑在一起?你能指导我怎么做吗?我正在使用 Netbeans。
编辑:
这是预加载器:
import javafx.application.Preloader;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Splash extends Preloader {
ProgressIndicator bar;
ImageView Background;
Stage stage;
private Scene createPreloaderScene() {
bar = new ProgressIndicator();
bar.setLayoutX(380);
bar.setLayoutY(250);
bar.setPrefSize(60, 60);
Background = new ImageView("Images/Splash.png");
Background.setEffect(null);
Pane p = new Pane();
p.setStyle("-fx-background-color: transparent;");
p.getChildren().addAll(Background, bar);
Scene scene = new Scene(p, 794, 587);
scene.setFill(null);
scene.getStylesheets().add(Scrap2.class.getResource("CSS/Progress.css").toExternalForm());
bar.setId("myprogress");
return scene;
}
@Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.initStyle(StageStyle.TRANSPARENT);
stage.show();
}
@Override
public void handleStateChangeNotification(StateChangeNotification scn) {
if (scn.getType() == StateChangeNotification.Type.BEFORE_START) {
stage.hide();
}
}
@Override
public void handleProgressNotification(ProgressNotification pn) {
bar.setProgress(pn.getProgress());
}
@Override
public void handleApplicationNotification(PreloaderNotification arg0) {
if (arg0 instanceof ProgressNotification) {
ProgressNotification pn= (ProgressNotification) arg0;
bar.setProgress(pn.getProgress());
}
}
}
这是我的主程序的第一部分:
@Override
public void init(){
/*Root*/
root = new Pane();
root.setStyle("-fx-background-color: transparent;");
root.setLayoutX(150);
notifyPreloader(new Preloader.ProgressNotification(0.1));
/*Create Background*/
createBinding(stage);
createContents();
createSaveMessages();
createFlipBook();
notifyPreloader(new Preloader.ProgressNotification(0.2));
/*Add Pages*/
createOverview();
createAccounts();
notifyPreloader(new Preloader.ProgressNotification(0.3));
createCounselors();
createInsurance();
notifyPreloader(new Preloader.ProgressNotification(0.4));
createAssets();
createPapers();
notifyPreloader(new Preloader.ProgressNotification(0.5));
createLoans();
createFuneral();
notifyPreloader(new Preloader.ProgressNotification(0.6));
createWills();
addAllPages();
notifyPreloader(new Preloader.ProgressNotification(0.7));
/*Add Toolbar on top*/
createToolBar();
notifyPreloader(new Preloader.ProgressNotification(0.9));
/*Create Opening Instructions*/
opening();
/*Load Saved Data*/
load();
notifyPreloader(new Preloader.ProgressNotification(1.0));
}
@Override
public void start(Stage stage) {
/*Scene*/
scene = new Scene(root, 1200, 700);
stage.setScene(scene);
scene.setFill(null);
/*Stage*/
this.stage = stage;
stage.initStyle(StageStyle.TRANSPARENT);
stage.centerOnScreen();
stage.show();
}
此示例仅适用于安装程序 exe/msi/image(没有 Mac 来测试 dmg)。这一步一步假设您已经安装了所需的工具,如 InnoSetup、Wix 工具集等。它还假设您已经使用 netbeans 配置工具 运行(设置路径、编辑配置文件等) .
先决条件:
- Inno Setup for .exe 包,下载 unicode 版本:http://www.jrsoftware.org/isdl.php
- .msi 包的 Wix 工具集:http://wixtoolset.org/
- 为 Inno Setup 和 Wix Toolset 设置 Windows 路径
第 1 步:
我在 Netbeans 中创建了一个新的 JavaFX 应用程序项目,如下所示:
第 2 步:
然后我给项目起了个名字,说向导也应该用给定的名称创建一个预加载器项目。此外,它应该在给定的包名称中创建一个应用程序 class。
第 3 步:
之后,我右键单击应用程序项目并 select 在部署下 "Enable Native Packaging"。
第 4 步:
在第 4 步中,我为应用程序创建了代码。预加载器将在 init() 方法中更新,并且仅在此处更新。应用程序初始化的所有工作都应放在此处。
JavaFXPreloaderApp.java
import javafx.application.Application;
import javafx.application.Preloader;
import javafx.event.ActionEvent;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class JavaFXPreloaderApp extends Application {
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(createContent(), 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public Parent createContent() {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane();
root.getChildren().add(btn);
return root;
}
@Override
public void init() throws Exception {
// A time consuming task simulation
final int max = 10;
for (int i = 1; i <= max; i++) {
notifyPreloader(new Preloader.ProgressNotification(((double) i) / max));
Thread.sleep(500);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
第 5 步:
唯一缺少的部分是预加载器代码。寻找唯一需要的方法 handleApplicationNotification,所有其他方法,如 handleProgressNotification 或 handleStateChangeNotification,您可以安全地删除,或将它们设为空存根。
JavaFXPreloader.java
import javafx.application.Preloader;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
/**
* Simple Preloader Using the ProgressBar Control
*/
public class JavaFXPreloader extends Preloader {
ProgressBar bar;
Stage stage;
private Scene createPreloaderScene() {
bar = new ProgressBar();
BorderPane p = new BorderPane();
p.setCenter(bar);
return new Scene(p, 300, 150);
}
@Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.show();
}
@Override
public void handleApplicationNotification(PreloaderNotification info) {
// Check if info is ProgressNotification
if (info instanceof ProgressNotification) {
// if yes, get the info and cast it
ProgressNotification pn = (ProgressNotification) info;
// update progress
bar.setProgress(pn.getProgress());
// if this was the last progress (progress reached 1), hide preloader
// this is really important, if preloader isn't hide until app loader
// reaches the start method of application and tries to open the stage of
// the main app with the show() method, it will not work.
if (pn.getProgress() == 1.0) {
stage.hide();
}
}
}
}
第 6 步:
现在是时候将应用程序捆绑到本机包中了(图片 only/exe/msi)。我右键单击应用程序项目并 select 编辑包以一个一个地创建。
第 7 步:
选择打包为图片后,您的目录应该如下所示:
第 8 步:
在您的目录中深入挖掘后,您应该找到图像:
第 9 步:
双击 .exe 文件应该会启动您的应用程序:
备注:
您可能犯的最大错误是在应用程序启动方法中调用内容。通常所有这些都必须在应用程序 init 方法中完成,在那里你加载巨大的文件,在那里你将连接到数据库,或者你在那里加载一个带有大量 css 或 fxml 文件的巨大自定义布局。还有就是要和预加载器说再见的地方(progress = 1)。尽量不要在应用程序启动方法中的预加载器处执行操作。别以为在Thread里,preloader是在main stage显示前做的,所以按顺序加载。
我有一个使用预加载器的 JavaFX 应用程序。我想做的是将其打包为本机包(Mac 应用程序或 Windows 包含 Java JDK 副本的 exe 文件)以便用户在他们的计算机上没有正确版本的 Java 仍然可以 运行 该应用程序。我已按照 creating native bundles and for adding preloaders 的 Oracle 说明进行操作。我得到的正是您所期望的 — 运行 是我的程序的本机包。
问题是捆绑包完全忽略了我的预加载器。它只是 运行 的主程序(经过很长的加载时间)。我知道包含预加载器,因为当我单独 运行 jar 文件时,它会显示出来。
有没有人成功地将 JavaFX 应用程序与预加载器捆绑在一起?你能指导我怎么做吗?我正在使用 Netbeans。
编辑:
这是预加载器:
import javafx.application.Preloader;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Splash extends Preloader {
ProgressIndicator bar;
ImageView Background;
Stage stage;
private Scene createPreloaderScene() {
bar = new ProgressIndicator();
bar.setLayoutX(380);
bar.setLayoutY(250);
bar.setPrefSize(60, 60);
Background = new ImageView("Images/Splash.png");
Background.setEffect(null);
Pane p = new Pane();
p.setStyle("-fx-background-color: transparent;");
p.getChildren().addAll(Background, bar);
Scene scene = new Scene(p, 794, 587);
scene.setFill(null);
scene.getStylesheets().add(Scrap2.class.getResource("CSS/Progress.css").toExternalForm());
bar.setId("myprogress");
return scene;
}
@Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.initStyle(StageStyle.TRANSPARENT);
stage.show();
}
@Override
public void handleStateChangeNotification(StateChangeNotification scn) {
if (scn.getType() == StateChangeNotification.Type.BEFORE_START) {
stage.hide();
}
}
@Override
public void handleProgressNotification(ProgressNotification pn) {
bar.setProgress(pn.getProgress());
}
@Override
public void handleApplicationNotification(PreloaderNotification arg0) {
if (arg0 instanceof ProgressNotification) {
ProgressNotification pn= (ProgressNotification) arg0;
bar.setProgress(pn.getProgress());
}
}
}
这是我的主程序的第一部分:
@Override
public void init(){
/*Root*/
root = new Pane();
root.setStyle("-fx-background-color: transparent;");
root.setLayoutX(150);
notifyPreloader(new Preloader.ProgressNotification(0.1));
/*Create Background*/
createBinding(stage);
createContents();
createSaveMessages();
createFlipBook();
notifyPreloader(new Preloader.ProgressNotification(0.2));
/*Add Pages*/
createOverview();
createAccounts();
notifyPreloader(new Preloader.ProgressNotification(0.3));
createCounselors();
createInsurance();
notifyPreloader(new Preloader.ProgressNotification(0.4));
createAssets();
createPapers();
notifyPreloader(new Preloader.ProgressNotification(0.5));
createLoans();
createFuneral();
notifyPreloader(new Preloader.ProgressNotification(0.6));
createWills();
addAllPages();
notifyPreloader(new Preloader.ProgressNotification(0.7));
/*Add Toolbar on top*/
createToolBar();
notifyPreloader(new Preloader.ProgressNotification(0.9));
/*Create Opening Instructions*/
opening();
/*Load Saved Data*/
load();
notifyPreloader(new Preloader.ProgressNotification(1.0));
}
@Override
public void start(Stage stage) {
/*Scene*/
scene = new Scene(root, 1200, 700);
stage.setScene(scene);
scene.setFill(null);
/*Stage*/
this.stage = stage;
stage.initStyle(StageStyle.TRANSPARENT);
stage.centerOnScreen();
stage.show();
}
此示例仅适用于安装程序 exe/msi/image(没有 Mac 来测试 dmg)。这一步一步假设您已经安装了所需的工具,如 InnoSetup、Wix 工具集等。它还假设您已经使用 netbeans 配置工具 运行(设置路径、编辑配置文件等) .
先决条件:
- Inno Setup for .exe 包,下载 unicode 版本:http://www.jrsoftware.org/isdl.php
- .msi 包的 Wix 工具集:http://wixtoolset.org/
- 为 Inno Setup 和 Wix Toolset 设置 Windows 路径
第 1 步:
我在 Netbeans 中创建了一个新的 JavaFX 应用程序项目,如下所示:
第 2 步:
然后我给项目起了个名字,说向导也应该用给定的名称创建一个预加载器项目。此外,它应该在给定的包名称中创建一个应用程序 class。
第 3 步:
之后,我右键单击应用程序项目并 select 在部署下 "Enable Native Packaging"。
第 4 步:
在第 4 步中,我为应用程序创建了代码。预加载器将在 init() 方法中更新,并且仅在此处更新。应用程序初始化的所有工作都应放在此处。
JavaFXPreloaderApp.java
import javafx.application.Application;
import javafx.application.Preloader;
import javafx.event.ActionEvent;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class JavaFXPreloaderApp extends Application {
@Override
public void start(Stage primaryStage) {
Scene scene = new Scene(createContent(), 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public Parent createContent() {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
System.out.println("Hello World!");
});
StackPane root = new StackPane();
root.getChildren().add(btn);
return root;
}
@Override
public void init() throws Exception {
// A time consuming task simulation
final int max = 10;
for (int i = 1; i <= max; i++) {
notifyPreloader(new Preloader.ProgressNotification(((double) i) / max));
Thread.sleep(500);
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
第 5 步:
唯一缺少的部分是预加载器代码。寻找唯一需要的方法 handleApplicationNotification,所有其他方法,如 handleProgressNotification 或 handleStateChangeNotification,您可以安全地删除,或将它们设为空存根。
JavaFXPreloader.java
import javafx.application.Preloader;
import javafx.application.Preloader.ProgressNotification;
import javafx.application.Preloader.StateChangeNotification;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
/**
* Simple Preloader Using the ProgressBar Control
*/
public class JavaFXPreloader extends Preloader {
ProgressBar bar;
Stage stage;
private Scene createPreloaderScene() {
bar = new ProgressBar();
BorderPane p = new BorderPane();
p.setCenter(bar);
return new Scene(p, 300, 150);
}
@Override
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.show();
}
@Override
public void handleApplicationNotification(PreloaderNotification info) {
// Check if info is ProgressNotification
if (info instanceof ProgressNotification) {
// if yes, get the info and cast it
ProgressNotification pn = (ProgressNotification) info;
// update progress
bar.setProgress(pn.getProgress());
// if this was the last progress (progress reached 1), hide preloader
// this is really important, if preloader isn't hide until app loader
// reaches the start method of application and tries to open the stage of
// the main app with the show() method, it will not work.
if (pn.getProgress() == 1.0) {
stage.hide();
}
}
}
}
第 6 步:
现在是时候将应用程序捆绑到本机包中了(图片 only/exe/msi)。我右键单击应用程序项目并 select 编辑包以一个一个地创建。
第 7 步:
选择打包为图片后,您的目录应该如下所示:
第 8 步:
在您的目录中深入挖掘后,您应该找到图像:
第 9 步:
双击 .exe 文件应该会启动您的应用程序:
备注:
您可能犯的最大错误是在应用程序启动方法中调用内容。通常所有这些都必须在应用程序 init 方法中完成,在那里你加载巨大的文件,在那里你将连接到数据库,或者你在那里加载一个带有大量 css 或 fxml 文件的巨大自定义布局。还有就是要和预加载器说再见的地方(progress = 1)。尽量不要在应用程序启动方法中的预加载器处执行操作。别以为在Thread里,preloader是在main stage显示前做的,所以按顺序加载。