66

我的应用程序是基于 Swing 的。我想介绍 JavaFX 并将其配置为在辅助显示器上呈现场景。我可以使用 JFrame 来保存可以保存 JFXPanel 的 JFXPanel,但我想使用 JavaFX API 来实现这一点。

子类化 com.sun.glass.ui.Application 并使用 Application.launch(this) 不是一个选项,因为调用线程将被阻塞。

从 Swing EDT 实例化阶段时,我得到的错误是:

java.lang.IllegalStateException: Toolkit not initialized

任何指针?


编辑:结论

问题:重要的 Swing GUI 应用程序需要运行 JavaFX 组件。应用程序的启动过程在启动依赖服务层后初始化 GUI。

解决方案

子类化 JavaFX Application 类并在单独的线程中运行它,例如:

public class JavaFXInitializer extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        // JavaFX should be initialized
        someGlobalVar.setInitialized(true);
    }
}

旁注:因为 Application.launch() 方法将 aClass<? extends Application>作为参数,所以必须使用全局变量来表示 JavaFX 环境已初始化。

替代方法:在 Swing Event Dispatcher Thread 中实例化 JFXPanel

final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        new JFXPanel(); // initializes JavaFX environment
        latch.countDown();
    }
});
latch.await();

通过使用这种方法,调用线程将等待 JavaFX 环境建立。

选择您认为合适的任何解决方案。我选择了第二个,因为它不需要全局变量来表示 JavaFX 环境的初始化,也不会浪费线程。

4

8 回答 8

23

Found a solution. If I just create a JFXPanel from Swing EDT before invoking JavaFX Platform.runLater it works. I don't know how reliable this solution is, I might choose JFXPanel and JFrame if turns out to be unstable.

public class BootJavaFX {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new JFXPanel(); // this will prepare JavaFX toolkit and environment
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        StageBuilder.create()
                                .scene(SceneBuilder.create()
                                        .width(320)
                                        .height(240)
                                        .root(LabelBuilder.create()
                                                .font(Font.font("Arial", 54))
                                                .text("JavaFX")
                                                .build())
                                        .build())
                                .onCloseRequest(new EventHandler<WindowEvent>() {
                                    @Override
                                    public void handle(WindowEvent windowEvent) {
                                        System.exit(0);
                                    }
                                })
                                .build()
                                .show();
                    }
                });
            }
        });
    }
}
于 2012-06-30T20:19:15.857 回答
21

使用 JavaFX 的唯一方法是继承 Application 或使用 JFXPanel,这正是因为它们准备了 env 和工具包。

阻塞线程可以通过使用来解决new Thread(...)

如果您在与 Swing/AWT 相同的 VM 中使用 JavaFX,我建议使用 JFXPanel,但您可以在此处找到更多详细信息:是否可以将 AWT 与 JavaFx 一起使用?

于 2012-06-30T16:27:24.260 回答
18

从 JavaFX 9 开始,您可以在不扩展Application类的情况下运行 JavaFX 应用程序,方法是调用Platform.startup()

Platform.startup(() ->
{
    // This block will be executed on JavaFX Thread
});

此方法启动 JavaFX 运行时。

于 2018-12-13T10:58:01.827 回答
8

我检查了源代码,这是初始化它

com.sun.javafx.application.PlatformImpl.startup(()->{});

并退出它

com.sun.javafx.application.PlatformImpl.exit();
于 2017-10-26T14:11:14.017 回答
4

还有一种方法可以显式初始化工具包,方法是调用: com.sun.javafx.application.PlatformImpl#startup(Runnable)

由于使用了 *Impl,有点 hacky,但如果您不想使用ApplicationJXFPanel出于某种原因,它很有用。

从这篇文章重新发布自己

于 2016-08-10T21:04:46.563 回答
4

我在创建单元测试以测试 javaFX tableview 更新时使用了以下内容

public class testingTableView {
        @BeforeClass
        public static void initToolkit() throws InterruptedException
        {
            final CountDownLatch latch = new CountDownLatch(1);
            SwingUtilities.invokeLater(() -> {
                new JFXPanel(); // initializes JavaFX environment
                latch.countDown();
            });

            if (!latch.await(5L, TimeUnit.SECONDS))
                throw new ExceptionInInitializerError();
        }

        @Test
        public void updateTableView() throws Exception {

            TableView<yourclassDefiningEntries> yourTable = new TableView<>();
            .... do your testing stuff

        }
    }

即使这篇文章与测试无关,但它帮助我让我的单元测试工作

  • 如果没有 BeforeClass initToolkit,那么单元测试中 TableView 的实例化将产生缺少工具包的消息
于 2016-07-13T07:41:59.223 回答
0
private static Thread thread;

public static void main(String[] args) {

    Main main = new Main();
    startup(main);
    thread = new Thread(main);
    thread.start();
}

public static void startup(Runnable r) {
    com.sun.javafx.application.PlatformImpl.startup(r);
}

@Override
public void run() {
    SoundPlayer.play("BelievexBelieve.mp3");
}

这是我的解决方案。该类名为 Main 并实现 Runnable。方法startup(Runnable r)是关键。

于 2018-05-12T02:11:40.170 回答
0

使用 Jack Lin 的回答,我发现它触发了 run() 两次。通过一些修改也使答案更加简洁,我提供以下内容;

import com.sun.javafx.application.PlatformImpl;

public class MyFxTest implements Runnable {

    public static void main(String[] args) {

        MyFxTest main = new MyFxTest();
        PlatformImpl.startup((Runnable) main);
    }

    @Override
    public void run() {

        // do your testing;
        System.out.println("Here 'tis");
        System.exit(0); // Optional
    }
}
于 2021-09-28T22:49:01.180 回答