这是基本的fxml文件:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.github.parboiled1.grappa.debugger.basewindow.BaseWindowUi">
<top>
<MenuBar BorderPane.alignment="CENTER">
<Menu mnemonicParsing="false" text="File">
<MenuItem mnemonicParsing="false"
text="New window" onAction="#newWindowEvent"/>
<MenuItem mnemonicParsing="false"
text="Close" onAction="#closeWindowEvent"/>
</Menu>
</MenuBar>
</top>
<center>
<Label text="Hello"/>
</center>
</BorderPane>
BaseWindowUi
类过于简单:
public class BaseWindowUi
{
private BaseWindowPresenter presenter;
public void init(final BaseWindowPresenter presenter)
{
this.presenter = presenter;
}
@FXML
void newWindowEvent(final ActionEvent event)
{
presenter.handleNewWindow();
}
@FXML
public void closeWindowEvent(final ActionEvent event)
{
presenter.handleCloseWindow();
}
}
主持人也是如此:
public final class BaseWindowPresenter
{
private final BaseWindowFactory windowFactory;
public BaseWindowPresenter(final BaseWindowFactory windowFactory)
{
this.windowFactory = windowFactory;
}
public void handleCloseWindow()
{
windowFactory.close(this);
}
public void handleNewWindow()
{
windowFactory.createWindow();
}
}
BaseWindowFactory
是一个只有一个具体实现的接口:
// Interface...
public interface BaseWindowFactory
{
void createWindow(Stage stage);
default void createWindow()
{
createWindow(new Stage());
}
void close(BaseWindowPresenter presenter);
}
// Implementation...
public final class DefaultBaseWindowFactory
implements BaseWindowFactory
{
private static final AlertFactory ALERT_FACTORY = new AlertFactory();
private static final URL MAIN_WINDOW_FXML;
static {
try {
MAIN_WINDOW_FXML = DefaultBaseWindowFactory.class.getResource(
"/baseWindow.fxml");
if (MAIN_WINDOW_FXML == null)
throw new IOException("base window fxml not found");
} catch (IOException e) {
ALERT_FACTORY.showError("Fatal error", "cannot load base FXML", e);
throw new ExceptionInInitializerError(e);
}
}
private final Map<BaseWindowPresenter, Stage> windows
= new HashMap<>();
private final AtomicInteger windowCount = new AtomicInteger();
@Override
public void createWindow(final Stage stage)
{
final FXMLLoader loader = new FXMLLoader(MAIN_WINDOW_FXML);
final Pane pane;
try {
pane = loader.load();
} catch (IOException e) {
ALERT_FACTORY.showError("Fatal error", "unable to create window",
e);
return;
}
final BaseWindowPresenter presenter = new BaseWindowPresenter(this);
final BaseWindowUi ui = loader.getController();
ui.init(presenter);
stage.setScene(new Scene(pane));
stage.setTitle("window " + windowCount.getAndIncrement());
windows.put(presenter, stage);
stage.show();
}
@Override
public void close(final BaseWindowPresenter presenter)
{
windows.get(presenter).close();
}
}
最后,这是实施Application
的课程:
public final class TestApp
extends Application
{
private final BaseWindowFactory factory = new DefaultBaseWindowFactory();
@Override
public void start(final Stage primaryStage)
throws Exception
{
factory.createWindow(primaryStage);
}
public static void main(final String... args)
{
launch(args);
}
}
所有这些工作;我可以打开新窗口,关闭从&#34;主要阶段&#34;创建的窗口,当所有窗口关闭等时应用程序正确退出。
那么,初级阶段的特殊性如何?此外,Application
的文档说明了这一点:
JavaFX创建一个应用程序线程,用于运行应用程序启动方法,处理输入事件和运行动画时间轴。必须在JavaFX应用程序线程上创建JavaFX场景和舞台对象以及将场景图形操作修改为活动对象(已经附加到场景的那些对象)。
我的代码目前有效,但它几乎没有任何作用;我最终遇到了应该在应用程序线程中运行的代码问题,但不是吗?所有new Stage()
都自动了解应用程序线程吗?
答案 0 :(得分:5)
主要阶段
关于primaryStage的唯一特别之处在于(与任何其他阶段不同),它是由JavaFX系统创建的,而不是由您的应用程序创建的。但除此之外,初级阶段的行为与其他阶段相似。
有与application lifecycle相关的阶段相关规则:
Platform.exit()
上的implicitExit
属性为真因此,如果您从未在应用程序中显示primaryStage(或任何其他阶段)并且您具有默认的implicitExit设置,那么您的应用程序将永远不会退出,因此请始终确保调用primaryStage.show()。
也许主要阶段对您来说似乎很特别,因为它恰好是您关闭的最后一个窗口,因此应用程序在关闭后会自动退出。但是如果其他一个窗口是最后一个关闭的窗口,那么应用程序就会退出,所以初级阶段在这方面并不是特别的。
线程
实际上只有一条与线程相关的规则,您已经在问题中引用了它:
您提供的代码不会引入任何新线程,它只使用JavaFX系统(JavaFX应用程序线程)传递给它的线程。因此,您提供的代码永远不会违反上述规则(正如Vitomir在其评论中指出的那样)。
你的DefaultBaseWindowFactory createWindow方法应该只在JavaFX应用程序线程上运行,因此你不需要像AtomicInteger这样的并发实用程序来封装windows的数量,一个简单的整数就足以作为读取或写入的唯一线程value将在JavaFX应用程序线程上运行。
您是否应该引入创建新线程的新代码(您编写的代码或代码库中的代码),请注意此代码不会修改活动场景图中的对象,也不会尝试直接从另一个创建窗口线程(如果有疑问,您可以通过Platform
轻松检查您正在执行的线程)。如果您最终拥有一些多线程代码,请使用Platform.runLater()
来包含对在SceneGraph中操作节点的函数的任何调用或创建新窗口,或使用JavaFX concurrency utilities来管理并发服务。 / p>
答案 1 :(得分:0)
实际上存在一个(重要的非显而易见的)差异:用户可以通过单击窗口装饰中的最小化按钮来最小化主阶段。对于其他阶段,窗口最小化按钮被禁用。我们必须以不同的方式设计我们的应用程序以解决这个问题。
使用Java 1.8.0_121进行测试。