为具有预加载器的 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 配置工具 运行(设置路径、编辑配置文件等) .

先决条件:

第 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,所有其他方法,如 handleProgressNotificationhandleStateChangeNotification,您可以安全地删除,或将它们设为空存根。

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显示前做的,所以按顺序加载。