【问题标题】:Create a native bundle for a JavaFX application that has a preloader为具有预加载器的 JavaFX 应用程序创建本机包
【发布时间】:2015-06-21 15:09:15
【问题描述】:

我有一个使用预加载器的 JavaFX 应用程序。我想做的是将它打包为一个本地包(Mac 应用程序或包含 Java JDK 副本的 Windows exe 文件),以便计算机上没有正确 Java 版本的用户仍然可以运行应用程序。我已经按照 creating native bundlesadding 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();
}

【问题讨论】:

  • 如果是netbeans javafx项目,只需在属性中激活原生打包即可。如果您需要更自定义的方式,请使用 bin 目录中的 javapackager Javapackager
  • 我认为Netbeans添加预加载器时有问题。在 Properties
  • 您必须提供解决方案的最小代码示例(预加载器/应用程序)。请发布您的预加载器和应用程序(可能只是摘录)。我的两分钱:不要将预加载器用于本机捆绑桌面应用程序。预加载器旨在为 JNLP 完美工作。请阅读Application Startup,而不是您可能只想要splash screen solution
  • 当我双击 jar 时,预加载器仍然有效。也许我应该问如何使 jar 文件使用指定的 JRE 运行。我已经有了应用程序结构——只是启动器文件似乎搞砸了。
  • 刚刚尝试了 Jewelsea 的解决方案。这是一个好主意,但它太慢了。预加载器的好处——它可以快速启动——被放在主启动方法中就失去了。

标签: java javafx native preloader


【解决方案1】:

此示例仅适用于安装程序 exe/msi/image(没有 Mac 来测试 dmg)。这一步一步假设您已经安装了所需的工具,如 InnoSetup、Wix Toolset 等。它还假设您已将工具配置为与 netbeans 一起运行(设置路径、编辑配置文件等)。

先决条件:

第 1 步:

我在 Netbeans 中创建了一个新的 JavaFX 应用程序项目,如下所示:

第 2 步:

然后我给项目起了一个名字,说向导也应该用给定的名字创建一个预加载器项目。此外,它应该在给定的包名中创建一个应用程序类。

第三步:

之后,我右键单击应用程序项目并在部署下选择“启用本机打包”。

第四步:

在第 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);
  }
}

第五步:

唯一缺少的部分是预加载器代码。查找唯一需要的方法 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 步:

现在是时候将应用程序捆绑到本机包(仅映像/exe/msi)了。我在applicaton项目上点右键,选择要一一创建的包。

第 7 步:

选择打包为图像后,您的目录应如下所示:

第 8 步:

在您的目录中深入挖掘之后,您应该会找到图像:

第 9 步:

双击 .exe 文件应该会启动您的应用程序:

备注:

您可能犯的最大错误是,在您的应用程序启动方法中调用事物。通常,所有这些都必须在应用程序的 init 方法中完成,在那里你加载巨大的文件,在那里你将连接到数据库,或者在那里你加载一个包含大量 css 或 fxml 文件的巨大自定义布局。还有一个可以和预加载器说再见的地方(进度= 1)。尽量不要在应用程序启动方法中的预加载器上做任何事情。不要以为在 Thread 中,preloader 是在主阶段显示之前就已经完成的事情,所以要按顺序加载。

【讨论】:

  • 我得到了同样的行为。预加载器在 jar 文件单独运行时起作用,但在通过 App 运行时不起作用。你有没有打包你的测试程序,然后它工作了吗?
  • 如果我在 IDE 之外启动 jar,只显示预加载器并遍历进度,之后,预加载器阶段关闭,主阶段不会显示。如果我在任务管理器中显示,则不再运行 java 进程。
  • 也许您在主程序中遇到了异常。我的 jar 文件工作正常。破坏它的仍然是包装。
  • 完全更新了我的答案,现在一切正常。阅读 Preloader 类的 JavaDoc,其中提到预加载器应该在主应用程序的 init 方法中完成。 docs.oracle.com/javase/8/javafx/api/javafx/application/…
  • 感谢您的详细回复。我按照上面的方法,使用 Netbeans 生成的预加载器从头开始创建一个新项目。不幸的是,它只是不能作为 Mac 应用程序工作。预加载器仍然没有出现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 2018-09-27
  • 2015-03-06
  • 1970-01-01
相关资源
最近更新 更多