为具有预加载器的JavaFX应用程序创建本机包

时间:2015-06-21 15:09:16

标签: java javafx native preloader

我有一个使用预加载器的JavaFX应用程序。我想要做的是将其打包为本机包(Mac应用程序或包含Java JDK副本的Windows exe文件),以便那些在其计算机上没有正确版本的Java的用户仍然可以运行该应用程序。我已按照Oracles对creating native bundlesadding preloaders的说明进行操作。我得到的正是你所期望的 - 一个运行我程序的本机包。

问题是捆绑包完全忽略了我的预加载器。它只运行主程序(经过很长的加载时间)。我知道包含了预加载器,因为当我单独运行jar文件时,它会显示出来。

是否有人成功将JavaFX应用与预加载器捆绑在一起?你能指导我怎么做吗?我使用的是Netbeans。

编辑:

这是Preloader:

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();
}

1 个答案:

答案 0 :(得分:3)

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

Prerequirements:

第1步:

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

step1

第2步:

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

step2

第3步:

之后我右键单击了应用程序项目并在部署中选择"启用Native Native"。

step3

第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步:

现在是时候将应用程序捆绑到本机包(仅图像/ exe / msi)。我右键单击了applicaton项目并选择了逐个创建的包。

step6

第7步:

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

step7

第8步:

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

step8

第9步:

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

step9

说明:

你可以做的最大的错误是,在你的应用程序启动方法中调用东西。 Normaly都必须在应用程序初始化方法中完成,在那里加载巨大的文件,在那里你将连接到数据库,或者在那里加载一个包含大量css或fxml文件的巨大自定义布局。并且有一个地方可以向预加载器说再见(进度= 1)。尽量不要在应用程序启动方法中的预加载器上执行操作。不要在线程中思考,预加载器是在显示主阶段之前做的事情,所以按顺序加载。