Mon application est basée sur Swing. Je voudrais introduire JavaFX et le configurer pour qu'il rende une scène sur un affichage secondaire ..__ Je pourrais utiliser un JFrame pour contenir un JFXPanel pouvant contenir un JFXPanel mais j'aimerais réaliser cela avec l'API JavaFX.
Sous-classer com.Sun.glass.ui.Application et utiliser Application.launch (this) n'est pas une option car le thread appelant serait bloqué.
Lors de l'instanciation d'une scène à partir de Swing EDT, l'erreur que je reçois est la suivante:
Java.lang.IllegalStateException: Toolkit not initialized
Des pointeurs?
EDIT: Conclusions
Problème : L'application graphique Swing non triviale doit exécuter les composants JavaFX. Le processus de démarrage de l'application initialise l'interface graphique après le démarrage d'une couche de service dépendante.
Solutions
Sous-classe JavaFX Application class et l'exécuter dans un thread séparé, par exemple:
public class JavaFXInitializer extends Application {
@Override
public void start(Stage stage) throws Exception {
// JavaFX should be initialized
someGlobalVar.setInitialized(true);
}
}
Note: Étant donné que la méthode Application.launch () prend un Class<? extends Application>
en argument, il est nécessaire d'utiliser une variable globale pour signaler que l'environnement JavaFX a été initialisé.
Approche alternative: instanciez JFXPanel dans le thread du répartiteur d’événements Swing :
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new JFXPanel(); // initializes JavaFX environment
latch.countDown();
}
});
latch.await();
En utilisant cette approche, le thread appelant attendra que l'environnement JavaFX soit configuré.
Choisissez la solution qui vous convient. Je suis allé avec le second car il n'a pas besoin d'une variable globale pour signaler l'initialisation de l'environnement JavaFX et ne gaspille pas non plus de thread.
Trouvé une solution. Si je viens de créer un JFXPanel à partir de Swing EDT avant d'appeler JavaFX Platform.runLater cela fonctionne .. Je ne sais pas à quel point cette solution est fiable, je pourrais choisir JFXPanel et JFrame s'il s'avère instable.
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();
}
});
}
});
}
}
La seule façon de travailler avec JavaFX est de sous-classer Application ou d'utiliser JFXPanel, précisément parce qu'ils préparent env et toolkit.
Le blocage du thread peut être résolu en utilisant new Thread(...)
.
Bien que je suggère d'utiliser JFXPanel si vous utilisez JavaFX dans le même VM que Swing/AWT, vous pouvez trouver plus de détails ici: Puis-je utiliser AWT avec JavaFx?
J'ai vérifié le code source et c'est pour l'initialiser
com.Sun.javafx.application.PlatformImpl.startup(()->{});
et pour sortir
com.Sun.javafx.application.PlatformImpl.exit();
J'ai utilisé ce qui suit lors de la création d'unittests pour tester les mises à jour 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
}
}
bien que ce poste ne soit pas lié à un test, il m'a alors permis de travailler au mieux
Il y a aussi moyen d'initialiser explicitement le toolkit, en appelant: com.Sun.javafx.application.PlatformImpl#startup(Runnable)
Petit peu hacky, en raison de l'utilisation de * Impl, mais est utile, si vous ne voulez pas utiliser Application
ou JXFPanel
pour une raison quelconque.
me re-poster de ce post
Depuis JavaFX 9, vous pouvez exécuter l'application JavaFX sans étendre la classe Application
en appelant Platform.startup()
:
Platform.startup(() ->
{
// This block will be executed on JavaFX Thread
}
Cette méthode démarre le runtime JavaFX.
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");
}
Ceci est ma solution. La classe s'appelle Main et implémente Runnable. La méthode startup(Runnable r)
est la clé.