JavaFX Application : is the primary stage in . start() 有什么特别的吗?

标签 java javafx javafx-8

这是基本的 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):

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

所有这些工作;我可以打开新窗口,关闭从“初级阶段”创建的窗口,关闭所有窗口后应用程序正确退出等等。

那么,初级阶段有什么特别之处呢?

此外,Application 的文档是这样说的:

JavaFX creates an application thread for running the application start method, processing input events, and running animation timelines. Creation of JavaFX Scene and Stage objects as well as modification of scene graph operations to live objects (those objects already attached to a scene) must be done on the JavaFX application thread.

我的代码目前可以工作,但几乎什么也没做;我最终不会遇到应该在应用程序线程中运行但没有运行的代码问题吗?所有 new Stage() 都自动识别应用程序线程吗?

最佳答案

初级阶段

primaryStage 唯一的特别之处在于(与任何其他阶段不同),它是由 JavaFX 系统创建的,而不是由您的应用程序创建的。但除此之外,初级阶段的行为与其他阶段一样。

有一个与application lifecycle相关的阶段相关规则:

  • 等待应用程序完成,这在发生以下任一情况时发生:
    • 应用程序调用 Platform.exit() 或者
    • 最后一个窗口已关闭,Platform 上的implicitExit 属性为真

因此,如果您从不在您的应用程序中显示 primaryStage(或任何其他阶段)并且您具有默认的 implicitExit 设置,您的应用程序将永远不会退出,因此请始终确保调用 primaryStage.show()。

也许初级阶段对你来说似乎很特别,因为它恰好是你关闭的最后一个窗口,所以应用程序在关闭后会自动退出。但是,如果其他窗口是最后一个关闭的窗口,应用程序将同样退出,因此初级阶段在这方面并不是很特别。

线程

实际上只有一条与线程相关的规则,您已经在问题中引用了它:

  • 必须在 JavaFX 应用程序线程上创建 JavaFX Scene 和 Stage 对象,以及将场景图操作修改为 Activity 对象(那些已附加到场景的对象)。

您提供的代码没有引入任何新线程,它只是使用JavaFX 系统传递给它的线程(JavaFX 应用程序线程)。因此,您提供的代码永远不会违反上述规则(正如 Vitomir 在他的评论中指出的那样)。

您的 DefaultBaseWindowFactory createWindow 方法应该只在 JavaFX 应用程序线程上运行,因此您不需要并发实用程序(例如 AtomicInteger)来封装窗口数量,一个简单的整数就足以作为读取或写入的唯一线程值将在 JavaFX 应用程序线程上运行。

如果您引入了创建新线程的新代码(您编写的代码或来自库的代码),那么请注意此代码不会修改 Activity 场景图中的对象,也不会尝试直接从另一个创建窗口线程(如果有疑问,您可以通过 System.out.println(Thread.getName())轻松检查您正在执行的线程。如果您最终拥有一些多线程代码,请使用Platform.runLater() 包装对操作 SceneGraph 中的节点或创建新窗口的函数的任何调用,或使用 JavaFX concurrency utilities 管理并发服务。

关于JavaFX Application : is the primary stage in . start() 有什么特别的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27970417/

相关文章:

java - 我可以为 Oracle JDK 作为 java 编译器许可 GPLv2 或 v3 吗?

java - 将 retrofit 回调值转换为返回封装对象

java - 为什么这段通用代码会出现编译错误

javafx-2 - JavafX 中的组件图

java - getResourceAsStream();始终为 NULL (Netbeans)

带有自定义对象的 javafx CheckListView 以显示特定属性

java - 如何在 Class<?> 的对象上执行方法

java - 在 JavaFX 元素中使用 css

listview - 如何确定用户是否滚动到 ListView 的末尾

JavaFX - TextField 提示文本在更改颜色后不会在聚焦时被删除