JavaFX Preloader 从未从 main 中调用
JavaFX Preloader never called from main
我的 IDE 是 IntelliJ。我尝试 this 文档来学习预加载器,但由于某种原因,预加载器从未从我的主 class 中调用,甚至它的方法也被调用。
所以这是我的主要内容 class:
public class LongInitApp extends Application {
Stage stage;
BooleanProperty ready = new SimpleBooleanProperty(false);
private void longStart() {
//simulate long init in background
Task task = new Task<Void>() {
@Override
protected Void call() throws Exception {
int max = 10;
for (int i = 1; i <= max; i++) {
Thread.sleep(200);
notifyPreloader(new ProgressNotification(((double) i)/max));
}
ready.setValue(Boolean.TRUE);
notifyPreloader(new StateChangeNotification(
StateChangeNotification.Type.BEFORE_START));
return null;
}};
new Thread(task).start();
}
public static void main(String args[]) {
launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
longStart();
stage.setScene(new Scene(new Label("Application started"),400, 400));
ready.addListener(new ChangeListener<Boolean>(){
public void changed(
ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (Boolean.TRUE.equals(t1)) {
Platform.runLater(new Runnable() {
public void run() {
stage.show();
}});}}});;}}
还有我的预加载器class:
public class LongAppInitPreloader extends Preloader {
ProgressBar bar;
Stage stage;
boolean noLoadingProgress = true;
private Scene createPreloaderScene() {
bar = new ProgressBar(0);
BorderPane p = new BorderPane();
p.setCenter(bar);
return new Scene(p, 300, 150);
}
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.show();
}
@Override
public void handleProgressNotification(ProgressNotification pn) {
if (pn.getProgress() != 1.0 || !noLoadingProgress) {
bar.setProgress(pn.getProgress()/2);
if (pn.getProgress() > 0) {
noLoadingProgress = false;
}
}
}
@Override
public void handleStateChangeNotification(StateChangeNotification evt) {
//ignore, hide after application signals it is ready
}
@Override
public void handleApplicationNotification(PreloaderNotification pn) {
if (pn instanceof ProgressNotification) {
double v = ((ProgressNotification) pn).getProgress();
if (!noLoadingProgress) {
v = 0.5 + v/2;
}
bar.setProgress(v);
} else if (pn instanceof StateChangeNotification) {
stage.hide();
}
}
}
您也可以查看此 http://docs.oracle.com/javafx/2/deployment/preloaders.htm 文档。此代码属于示例 9-11 和 9-12。
您缺少 LongInitApp 中的 Main 方法 class
public static void main(String args[]) {
launch(args);
}
您需要在应用程序启动时指定预加载器 class。 "quick and dirty" 方法是使用非 public API class com.sun.javafx.application.LauncherImpl
。请注意,此 class 不能保证在 JavaFX 的未来版本中可用,因此您应该仅将其用于快速测试(如果有的话)。在您的 LongInitApp
class:
中使用以下主要方法
public static void main(String[] args) {
LauncherImpl.launchApplication(LongInitApp.class, LongAppInitPreloader.class, args);
}
包含预加载器的 "official" 方法是将其指定为 JavaFX 部署过程的一部分。部署过程的完整文档位于 http://docs.oracle.com/javase/8/docs/technotes/guides/deploy/,但最小的方法如下。
- 编译应用程序(您的 IDE 通常会在您保存时执行此操作)
从命令行,运行 javapackager
工具与 createjar
命令:
javapackager -createjar -outfile myapp.jar -appclass my.package.LongInitApp \
-preloader my.package.LongAppInitPreloader -srcdir dir
其中 my.package
是包含您的应用程序和预加载器的包 classes(它们可以在不同的包中)并且 dir
是包含您所有 classes(例如,如果 my.package
确实是你的包名,那么 dir
将有一个子目录 my
,它会有一个子目录 package
,它会包含 .class
个文件)。
这将生成一个 myapp.jar
文件,它是可执行的并且可以识别您的预加载器,因此您可以使用 java -jar myapp.jar
执行它。如果您有兴趣,可以从 jar 文件中提取生成的清单,然后使用 jar xf myapp.jar META-INF/MANIFEST.MF
查看其中的内容(然后查看文件 META-INF/MANIFEST.MF
)。 (简而言之,它所做的是将主要 class 声明为内部 class,旨在启动 JavaFX 应用程序。清单文件包含指定 JavaFX 的属性应用程序 class(在您的情况下为 LongInitApp
)和预加载器 class,如果存在的话。启动应用程序的内部 class 检索这些属性,其主要方法基本上启动您定义的应用程序,使用预加载器(如果存在)。)
请注意,如果您使用此方法,您的应用程序 class LongInitApp
不需要(可能 不应该 )main
方法.
大多数 IDE 对此都有某种形式的支持。例如。如果您将 Eclipse 与 E(fx)clipse 插件一起使用,并创建一个 JavaFX 项目,它将为您生成一个 build.fxbuild
文件。双击该文件将打开一个可视化编辑器,您可以在其中设置上面 javapackager
命令中定义的属性。单击 "Generate ant build.xml and run" 将创建 jar 文件。
除非您需要预加载器提供的特定功能,超出您可以轻松自行编程的功能,否则可能不值得为此付出努力。具体来说,如果您通过 Java Web Start 部署应用程序,那么预加载器特别有用,而且主 jar 文件的下载将花费很长时间。预加载器可以在发生时显示。如果您使用的是独立应用程序(或独立的应用程序包),那么您可以很容易地自己创建 "preloader" 功能。
例如,除非我使用 Java Web Start 并且主 jar 文件很大,否则我可能会通过使 "preloader" 成为常规的旧 class 来重构您的示例代码]:
import javafx.scene.Scene;
import javafx.beans.property.DoubleProperty ;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class LongAppInitPreloader {
private ProgressBar bar;
private Stage stage;
public LongAppInitPreloader() {
this.stage = new Stage();
stage.setScene(createPreloaderScene());
}
private Scene createPreloaderScene() {
bar = new ProgressBar(0);
BorderPane p = new BorderPane();
p.setCenter(bar);
return new Scene(p, 300, 150);
}
public void show() {
stage.show();
}
public void hide() {
stage.hide();
}
public DoubleProperty progressProperty() {
return bar.progressProperty();
}
}
然后您的应用程序 class 可以只使用任务并更新其进度:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class LongInitApp extends Application {
@Override
public void start(final Stage stage) throws Exception {
Task<Void> task = createLongStartTask();
stage.setScene(new Scene(new Label("Application started"), 400, 400));
LongAppInitPreloader preloader = new LongAppInitPreloader();
preloader.progressProperty().bind(task.progressProperty());
task.setOnSucceeded(e -> {
stage.show();
preloader.hide();
});
preloader.show();
new Thread(task).start();
}
private Task<Void> createLongStartTask() {
// simulate long init in background
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
int max = 10;
for (int i = 1; i <= max; i++) {
Thread.sleep(200);
updateProgress(i, max);
}
return null;
}
};
return task ;
}
// just here so I can run directly from Eclipse:
public static void main(String args[]) {
launch(args);
}
}
我的 IDE 是 IntelliJ。我尝试 this 文档来学习预加载器,但由于某种原因,预加载器从未从我的主 class 中调用,甚至它的方法也被调用。
所以这是我的主要内容 class:
public class LongInitApp extends Application {
Stage stage;
BooleanProperty ready = new SimpleBooleanProperty(false);
private void longStart() {
//simulate long init in background
Task task = new Task<Void>() {
@Override
protected Void call() throws Exception {
int max = 10;
for (int i = 1; i <= max; i++) {
Thread.sleep(200);
notifyPreloader(new ProgressNotification(((double) i)/max));
}
ready.setValue(Boolean.TRUE);
notifyPreloader(new StateChangeNotification(
StateChangeNotification.Type.BEFORE_START));
return null;
}};
new Thread(task).start();
}
public static void main(String args[]) {
launch(args);
}
@Override
public void start(final Stage stage) throws Exception {
longStart();
stage.setScene(new Scene(new Label("Application started"),400, 400));
ready.addListener(new ChangeListener<Boolean>(){
public void changed(
ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (Boolean.TRUE.equals(t1)) {
Platform.runLater(new Runnable() {
public void run() {
stage.show();
}});}}});;}}
还有我的预加载器class:
public class LongAppInitPreloader extends Preloader {
ProgressBar bar;
Stage stage;
boolean noLoadingProgress = true;
private Scene createPreloaderScene() {
bar = new ProgressBar(0);
BorderPane p = new BorderPane();
p.setCenter(bar);
return new Scene(p, 300, 150);
}
public void start(Stage stage) throws Exception {
this.stage = stage;
stage.setScene(createPreloaderScene());
stage.show();
}
@Override
public void handleProgressNotification(ProgressNotification pn) {
if (pn.getProgress() != 1.0 || !noLoadingProgress) {
bar.setProgress(pn.getProgress()/2);
if (pn.getProgress() > 0) {
noLoadingProgress = false;
}
}
}
@Override
public void handleStateChangeNotification(StateChangeNotification evt) {
//ignore, hide after application signals it is ready
}
@Override
public void handleApplicationNotification(PreloaderNotification pn) {
if (pn instanceof ProgressNotification) {
double v = ((ProgressNotification) pn).getProgress();
if (!noLoadingProgress) {
v = 0.5 + v/2;
}
bar.setProgress(v);
} else if (pn instanceof StateChangeNotification) {
stage.hide();
}
}
}
您也可以查看此 http://docs.oracle.com/javafx/2/deployment/preloaders.htm 文档。此代码属于示例 9-11 和 9-12。
您缺少 LongInitApp 中的 Main 方法 class
public static void main(String args[]) {
launch(args);
}
您需要在应用程序启动时指定预加载器 class。 "quick and dirty" 方法是使用非 public API class com.sun.javafx.application.LauncherImpl
。请注意,此 class 不能保证在 JavaFX 的未来版本中可用,因此您应该仅将其用于快速测试(如果有的话)。在您的 LongInitApp
class:
public static void main(String[] args) {
LauncherImpl.launchApplication(LongInitApp.class, LongAppInitPreloader.class, args);
}
包含预加载器的 "official" 方法是将其指定为 JavaFX 部署过程的一部分。部署过程的完整文档位于 http://docs.oracle.com/javase/8/docs/technotes/guides/deploy/,但最小的方法如下。
- 编译应用程序(您的 IDE 通常会在您保存时执行此操作)
从命令行,运行
javapackager
工具与createjar
命令:javapackager -createjar -outfile myapp.jar -appclass my.package.LongInitApp \ -preloader my.package.LongAppInitPreloader -srcdir dir
其中
my.package
是包含您的应用程序和预加载器的包 classes(它们可以在不同的包中)并且dir
是包含您所有 classes(例如,如果my.package
确实是你的包名,那么dir
将有一个子目录my
,它会有一个子目录package
,它会包含.class
个文件)。这将生成一个
myapp.jar
文件,它是可执行的并且可以识别您的预加载器,因此您可以使用java -jar myapp.jar
执行它。如果您有兴趣,可以从 jar 文件中提取生成的清单,然后使用jar xf myapp.jar META-INF/MANIFEST.MF
查看其中的内容(然后查看文件META-INF/MANIFEST.MF
)。 (简而言之,它所做的是将主要 class 声明为内部 class,旨在启动 JavaFX 应用程序。清单文件包含指定 JavaFX 的属性应用程序 class(在您的情况下为LongInitApp
)和预加载器 class,如果存在的话。启动应用程序的内部 class 检索这些属性,其主要方法基本上启动您定义的应用程序,使用预加载器(如果存在)。)请注意,如果您使用此方法,您的应用程序 class
LongInitApp
不需要(可能 不应该 )main
方法.
大多数 IDE 对此都有某种形式的支持。例如。如果您将 Eclipse 与 E(fx)clipse 插件一起使用,并创建一个 JavaFX 项目,它将为您生成一个 build.fxbuild
文件。双击该文件将打开一个可视化编辑器,您可以在其中设置上面 javapackager
命令中定义的属性。单击 "Generate ant build.xml and run" 将创建 jar 文件。
除非您需要预加载器提供的特定功能,超出您可以轻松自行编程的功能,否则可能不值得为此付出努力。具体来说,如果您通过 Java Web Start 部署应用程序,那么预加载器特别有用,而且主 jar 文件的下载将花费很长时间。预加载器可以在发生时显示。如果您使用的是独立应用程序(或独立的应用程序包),那么您可以很容易地自己创建 "preloader" 功能。
例如,除非我使用 Java Web Start 并且主 jar 文件很大,否则我可能会通过使 "preloader" 成为常规的旧 class 来重构您的示例代码]:
import javafx.scene.Scene;
import javafx.beans.property.DoubleProperty ;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class LongAppInitPreloader {
private ProgressBar bar;
private Stage stage;
public LongAppInitPreloader() {
this.stage = new Stage();
stage.setScene(createPreloaderScene());
}
private Scene createPreloaderScene() {
bar = new ProgressBar(0);
BorderPane p = new BorderPane();
p.setCenter(bar);
return new Scene(p, 300, 150);
}
public void show() {
stage.show();
}
public void hide() {
stage.hide();
}
public DoubleProperty progressProperty() {
return bar.progressProperty();
}
}
然后您的应用程序 class 可以只使用任务并更新其进度:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class LongInitApp extends Application {
@Override
public void start(final Stage stage) throws Exception {
Task<Void> task = createLongStartTask();
stage.setScene(new Scene(new Label("Application started"), 400, 400));
LongAppInitPreloader preloader = new LongAppInitPreloader();
preloader.progressProperty().bind(task.progressProperty());
task.setOnSucceeded(e -> {
stage.show();
preloader.hide();
});
preloader.show();
new Thread(task).start();
}
private Task<Void> createLongStartTask() {
// simulate long init in background
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
int max = 10;
for (int i = 1; i <= max; i++) {
Thread.sleep(200);
updateProgress(i, max);
}
return null;
}
};
return task ;
}
// just here so I can run directly from Eclipse:
public static void main(String args[]) {
launch(args);
}
}