Mixing Swing/FX: can't dispose a dialog from fxml controller

╄→尐↘猪︶ㄣ 提交于 2019-12-07 16:33:10

问题


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 :-)


回答1:


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




回答2:


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.




回答3:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!