可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
My application is Swing-based. I would like to introduce JavaFX and configure it to render a Scene on a secondary display. I could use a JFrame to hold a JFXPanel which could hold a JFXPanel but I would like to achieve this with JavaFX API.
Subclassing com.sun.glass.ui.Application and using Application.launch(this) is not an option because the invoking thread would be blocked.
When instantiating a Stage from Swing EDT, the error I get is:
java.lang.IllegalStateException: Toolkit not initialized
Any pointers?
EDIT: Conclusions
Problem: Non-trivial Swing GUI application needs to run JavaFX components. Application's startup process initializes the GUI after starting up a dependent service layer.
Solutions
Subclass JavaFX Application class and run it in a separate thread e.g.:
public class JavaFXInitializer extends Application { @Override public void start(Stage stage) throws Exception { // JavaFX should be initialized someGlobalVar.setInitialized(true); } }
Sidenote: Because Application.launch() method takes a Class extends Application>
as an argument, one has to use a global variable to signal JavaFX environment has been initialized.
Alternative approach: instantiate JFXPanel in Swing Event Dispatcher Thread:
final CountDownLatch latch = new CountDownLatch(1); SwingUtilities.invokeLater(new Runnable() { public void run() { new JFXPanel(); // initializes JavaFX environment latch.countDown(); } }); latch.await();
By using this approach the calling thread will wait until JavaFX environment is set up.
Pick any solution you see fit. I went with the second one because it doesn't need a global variable to signal the initialization of JavaFX environment and also doesn't waste a thread.
回答1:
The only way to work with JavaFX is to subclass Application or use JFXPanel, exactly because they prepare env and toolkit.
Blocking thread can be solved by using new Thread(...)
.
Although I suggest to use JFXPanel if you are using JavaFX in the same VM as Swing/AWT, you can find more details here: Is it OK to use AWT with JavaFx?
回答2:
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() { @Override public void handle(WindowEvent windowEvent) { System.exit(0); } }) .build() .show(); } }); } }); } }
回答3:
I used following when creating unittests for testing javaFX tableview updates
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 yourTable = new TableView(); .... do your testing stuff } }
even though this post is not test related, then it helped me to get my unittest to work
- without the BeforeClass initToolkit, then the instantiation of TableView in the unittest would yield a message of missing toolkit
回答4:
There's also way to initialize toolkit explicitly, by calling: com.sun.javafx.application.PlatformImpl#startup(Runnable)
Little bit hacky, due to using *Impl, but is useful, if you don't want to use Application
or JXFPanel
for some reason.
re-posting myself from this post
回答5:
I checked the source code and this is to initialize it
com.sun.javafx.application.PlatformImpl.startup(()->{});
and to exit it
com.sun.javafx.application.PlatformImpl.exit();