The scenario: the top-level container is a Swing JDialog which has some fx content, including a fx button that triggers a dispose of the button. Disposing works a expected (dialog is hidden) when the button is created and configured with the appropriate eventHandler manually. The dialog is not disposed when the button is created/configure via fxml. The example below contains both a manually configured and a fxml loaded/bound button to see the different behaviour.
Questions:
- anything wrong with the example?
- is there any difference in swing/fx interaction to be expected (manual vs. fxml)?
- how to make it work from fxml?
The code:
package fxml;
import java.io.IOException;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
public class DisposeExample {
@FXML
Button closeButton;
Button fxButton;
private JDialog dialog;
/**
* The action handler method used by fx buttons.
*/
public void onAction(final ActionEvent ac) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println("onAction: " +ac);
dialog.dispose();
}
});
}
protected Button createFxButton() {
Button fxButton = new Button("close from fx");
fxButton.setOnAction(new javafx.event.EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
onAction(event);
}
});
return fxButton;
}
public void initFX() {
final JFXPanel fxPanel = new JFXPanel();
dialog.add(fxPanel);
Platform.runLater(new Runnable() {
@Override
public void run() {
FlowPane parent = null;
try {
parent = FXMLLoader.load(DisposeExample.class.getResource(
"DisposeController.fxml"));
} catch (IOException e) {
e.printStackTrace();
}
fxButton = createFxButton();
parent.getChildren().add(fxButton);
Scene scene = new Scene(parent);
fxPanel.setScene(scene);
}
});
}
public DisposeExample() {
dialog = new JDialog();
dialog.setTitle("Simple Swing Dialog");
initFX();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JDialog example = new DisposeExample().dialog;
example.setSize(400, 400);
example.setVisible(true);
}
});
}
}
The fxml content:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<FlowPane id="Content" fx:id="windowPanel"
xmlns:fx="http://javafx.com/fxml" fx:controller="fxml.DisposeExample">
<children>
<Button fx:id="closeButton" onAction="#onAction"
prefHeight="35.0" prefWidth="300.0" text="Close (fxml controller)">
</Button>
</children>
</FlowPane>
BTW: there's a similar question from the beginning of this year, unanswered.
Edit
something weird going on: after running for a couple of minutes, it throws a OutOfMemoryError - something deeper down doesn't stop creating .. what?
java.lang.OutOfMemoryError: Java heap space
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2521)
at java.lang.Class.privateGetPublicMethods(Class.java:2641)
at java.lang.Class.privateGetPublicMethods(Class.java:2657)
at java.lang.Class.privateGetPublicMethods(Class.java:2657)
at java.lang.Class.privateGetPublicMethods(Class.java:2657)
at java.lang.Class.privateGetPublicMethods(Class.java:2657)
at java.lang.Class.privateGetPublicMethods(Class.java:2657)
at java.lang.Class.getMethods(Class.java:1457)
at sun.reflect.misc.MethodUtil.getMethods(MethodUtil.java:99)
at com.sun.javafx.fxml.BeanAdapter.updateMethodCache(BeanAdapter.java:265)
at com.sun.javafx.fxml.BeanAdapter.setBean(BeanAdapter.java:250)
at com.sun.javafx.fxml.BeanAdapter.<init>(BeanAdapter.java:213)
at javafx.fxml.FXMLLoader$Element.getValueAdapter(FXMLLoader.java:157)
at javafx.fxml.FXMLLoader$Element.getProperties(FXMLLoader.java:165)
at javafx.fxml.FXMLLoader$ValueElement.processValue(FXMLLoader.java:647)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:570)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2314)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2131)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2028)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2744)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2723)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2709)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2696)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2685)
at fxml.DisposeExample$3.run(DisposeExample.java:65)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
Edit
fyi: same behaviour in 8u113, so filed an issue in fx-jira, incurable optimist that I am :-)
Bug alert - remove the controller from the FXML-file and set it in the code instead.
FXMLLoader fxmlLoader = new FXMLLoader(DisposeExample.class.getResource("DisposeController.fxml"));
fxmlLoader.setController(DisposeExample.this);
parent = (FlowPane)fxmlLoader.load();
Unfortunately this will also destroy the FX-CPU-heating on these cold days ;-)
anything wrong with the example?
a resounding YES - citing Martin's (very quick and clear :-) comment to the issue:
The problem is in your fxml file.
"fx:controller" attribute takes the class and creates a new instance of it (using the default contructor). The default constructor of your DisposeExample class posts a new Runnable that will load the same fxml file again a create yet another instance of DisposeExample class.
You should either use a different class for your controller or set the controller manually using the setController() call or using a controller factory (setControllerFactory). Otherwise, there is no way for FXMLLoader to know that you wanted to use your particular DisposeExample object.
So why are you not executing the dispose on the swing thread?
来源:https://stackoverflow.com/questions/19889901/mixing-swing-fx-cant-dispose-a-dialog-from-fxml-controller