我正在尝试为我的程序创建一个启动画面,因为它需要一些时间才能启动。 问题是创建GUI需要一段时间(创建对话框,更新表等)。而且我无法将GUI创建移动到后台线程(如“Task”类),因为我将获得“Not on FXApplication Thread”异常。 我尝试使用:
Platform.runLater(new Runnable() {
public void run() {
//create GUI
}
}
任务的“调用”方法:
public class InitWorker extends Task<Void> {
private Model model;
private ViewJFX view;
public InitWorker(Model model) {
this.model = model;
}
@Override
protected Void call() throws Exception {
View view = new View();
Collection collection = new Collection();
//do stuff
}
}
当我在Swing中编写程序时,我可以在EventDispatchThread上显示和更新Splash Screen,而不需要任何真正的concurreny。代码看起来像这样:
public void build() {
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Menus");
menuCreator = new MenuCreatorOld (model, this);
menuCreator.createMenu();
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE, "Creating Toolbar");
toolBar = menuCreator.createToolBar();
createWesternPanelToolBar();
shoppingPanel = new ShoppingListOld(model, this, collectionController, shoppingController, controller);
centerTabbedPane = new JTabbedPane();
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Collection");
collectionPanel = new CollectionOld(model, collectionController, this, controller);
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Wish List");
wishPanel = new WishListOld(model, this, collectionController, wishController, controller);
MainOld.updateProgressBar(MainOld.PROGRESSBAR_VALUE++, "Creating Folders Table");
//and so on
}
public static void updateProgressBar(int progressValue, String text) {
System.out.println("Loading Bar Value:"+progressValue);
progressBar.setValue(progressValue);
loadingLabel.setText(text);
progressBar.setString(text);
}
在显示带加载栏的启动画面时,有没有办法在后台创建GUI?
编辑:
我查看了我的代码,并且能够将启动时间减少5秒。大多数对话框在创建数据时从数据库中提取数据。所以我将对话框的创建移动到了他们的getter方法中。这导致了3秒的改善。但我仍然想知道是否有在后台线程上创建GUI的方法。
编辑:
正如所建议的那样,我也尝试在“任务”中使用“RunLater”。 这样我就可以创建GUI并显示SplashScreen,但我无法更新进度条和进度标签,因为GUI创建会阻止JavaFX应用程序线程。只有在完全创建GUI后,才会更新进度条和标签。
这是一个你们可以运行的例子(我删除了启动画面,只保留了进度条和进度标签):
public class InitWorker extends Task<Void> {
private static ProgressBar progressBar;
private static Label progressLabel;
private static double PROGRESS_MAX = 5;
private double loadingValue;
public InitWorker() {
loadingValue = 0;
}
@Override
protected void succeeded() {
System.out.println("Succeeded");
}
@Override
protected void failed() {
System.out.println("Failed");
}
@Override
protected Void call() throws Exception {
System.out.println("RUNNING");
Platform.runLater(new Runnable() {
public void run() {
displaySplashScreen();
for(int i=0; i<10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
updateProgressBar(loadingValue++, "Label "+i);
Stage stage = new Stage();
Label label = new Label("Label " + i);
VBox panel = new VBox();
panel.getChildren().add(label);
Scene scene = new Scene(panel);
stage.setScene(scene);
stage.centerOnScreen();
stage.show();
}
// updateProgressBar(1, "Initializing...");
}});
return null;
}
public void updateProgressBar(double loadingValue, String text) {
progressBar.setProgress(loadingValue / PROGRESS_MAX);
progressLabel.setText(text);
}
public static void displaySplashScreen() {
Stage progressBarStage = new Stage();
progressBar = new ProgressBar();
Scene progressBarScene = new Scene(progressBar);
progressBarStage.setScene(progressBarScene);
Stage progressLabelStage = new Stage();
progressLabel = new Label("Loading...");
progressLabel.setPadding(new Insets(5));
progressLabel.setStyle("-fx-background-color: red");
Scene progressLabelScene = new Scene(progressLabel);
progressLabelStage.setScene(progressLabelScene);
double progressBarWidth = 500;
double progressBarHeight = 75;
//muss angezeigt werden, um sie abhängig von Größe zu positionieren
progressBarStage.show();
progressLabelStage.show();
//
progressBarStage.setWidth(progressBarWidth);
progressBarStage.setHeight(progressBarHeight);
progressBarStage.centerOnScreen();
progressBarStage.centerOnScreen();
progressLabelStage.setY(progressLabelStage.getY() + 25);
}
}
答案 0 :(得分:0)
请参阅Task documentation titled "A Task Which Modifies The Scene Graph",其中提供了一个示例:
final Group group = new Group();
Task<Void> task = new Task<Void>() {
@Override protected Void call() throws Exception {
for (int i=0; i<100; i++) {
if (isCancelled()) break;
final Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
Platform.runLater(new Runnable() {
@Override public void run() {
group.getChildren().add(r);
}
});
}
return null;
}
};
上面的示例通过100次runLater调用将矩形添加到场景图中。更有效的方法是将矩形添加到未附加到活动场景图的组中,然后仅将组添加到runLater调用中的活动场景图中。例如:
final Group groupInSceneGraph = new Group();
Task<Void> task = new Task<Void>() {
@Override protected Void call() throws Exception {
final Group localGroup = new Group();
for (int i=0; i<100; i++) {
if (isCancelled()) break;
final Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
localGroup.getChildren().add(r);
}
Platform.runLater(new Runnable() {
@Override public void run() {
groupInSceneGraph.add(localGroup);
}
});
return null;
}
};
只要对象没有附加到活动场景图,您就可以从JavaFX应用程序线程(包括加载FXML)创建和修改大多数场景图形对象。活动场景图是指当前作为场景附加到显示舞台的场景图。 (诸如WebView
之类的复杂控件可能是此规则的一个例外,可能需要在JavaFX应用程序线程上创建。
您只能将JavaFX应用程序线程创建的场景图对象附加到JavaFX应用程序线程上的活动场景图(例如使用Platform.runLater()
)。而且,只要它们继续附加到活动场景图上,您就必须在JavaFX应用程序线程上使用它们。