【问题标题】:JavaFX 2.2 JFXPanel in Swing application showing Swing modal dialogs显示 Swing 模式对话框的 Swing 应用程序中的 JavaFX 2.2 JFXPanel
【发布时间】:2014-02-19 18:10:23
【问题描述】:

我有一个完善的 Swing 应用程序,我正在使用 JFXPanel 部分升级到 JavaFX。带有 JFXPanel 的 JFrame 从另一个类中获取一个场景,该类在代码(不是 FXML)中创建一个根节点,它是一个 HBox,其左侧边栏非常简单,带有动态生成的链接、按钮和东西,右侧是根据用户在左侧按下的按钮动态填充内容。右侧内容由多组 FXML 和控制器类构建。

在这些右侧内容类中的每一个中,我必须提出一些标准的 Swing JDialogs 或 JOptionPane,它们在构造函数中接受父级(JFrame 或 JDialog),用于屏幕放置和模态。 (这些 JDialogs 和 JOptionPanes 来自我们多年来编写的常用组件库)

有没有办法在运行时让我获得对包含 JFXPanel 的 JFrame 的引用,以便我可以用适当的方式实例化它们?

** 编辑 **

  1. 模态确实有效
  2. 我现在真正想做的是将对话框置于父级的中心。当然,我相信这对我将来会有很多其他的原因。
  3. 这是一个缩略版。我已经能够运行它,所以它应该适合你。
  4. 如果这有点混乱,我深表歉意:-S
  5. 非常感谢您的帮助!

这是主类:

import java.awt.*;
import java.io.IOException;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.fxml.FXML;
import javax.swing.*;

public class SwingMain extends JFrame {

    public static final String TITLE = "Main JFrame";

    private GridBagLayout gridBagLayout1 = new GridBagLayout();

    private JPanel jPanel = new JPanel();

    private JFXPanel jfxPanel = new JFXPanel();
    private JFXSceneMaker sceneMaker = new JFXSceneMaker(this);

    public SwingMain() {
        super();
        try {
            init();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final SwingMain swingMainTest = new SwingMain();
        swingMainTest.setVisible(true);
    }

    void init() throws Exception {
        Dimension minimumSize = new Dimension(1280, 864);
        this.setMinimumSize(minimumSize);
        this.setPreferredSize(minimumSize);
        jPanel.setLayout(gridBagLayout1);
        this.setTitle(TITLE);

        Platform.setImplicitExit(false);
        try {
            prepareScene();
        } catch (Exception ex) {
            // log error
        }

        getContentPane().add(jPanel);
        jPanel.add(jfxPanel, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0,
                GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(
                        0, 0, 0, 0), 0, 0));
    }

    protected void prepareScene() {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                try {
                    Scene scene = sceneMaker.getScene();
                    jfxPanel.setScene(scene);
                } catch (IOException ioex) {
                    // log error
                }
            }
        });
    }
}

这是为 JFXPanel 提供场景的类

import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import javafx.animation.*;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.util.Duration;

public class JFXSceneMaker {
    private static final Interpolator interpolator = Interpolator.SPLINE(0.4829, 0.5709, 0.6803, 0.9928);

    // To obtain a scalable page layout, you define all positions and sizes in terms of “root em” (rem), 
    //  the em size of the default font
    private final double rem = Math.rint(new Text("").getLayoutBounds().getHeight());
    private double SIDEBAR_WIDTH = rem * 13;
    private Pane root;
    private StackPane currentPane, sparePane;
    private VBox sideBar;
    private int currentPaneIndex = 0;
    private Timeline timeline;
    private ContentPane nextPane = null;
    private int nextPaneIndex;
    private JFrame swingParent;

    public static final String TITLE = "JavaFX Rocks!";

    private ContentPane[] panes;

    public JFXSceneMaker(JFrame frame) {
        // This is a reference to the Swing parent JFrame that I want to place the JOptionPanes and JDialogs over
        this.swingParent = frame;
    }

    public Scene getScene() throws IOException {

        Parent pane1 = FXMLLoader.load(Pane1Controller.class.getResource("pane1contentPane.fxml"));
        Parent pane2 = FXMLLoader.load(Pane2Controller.class.getResource("pane2contentPane.fxml"));
        Parent pane3 = FXMLLoader.load(Pane3Controller.class.getResource("pane3contentPane.fxml"));

        panes = new ContentPane[]{
            new ContentPane("Panel1", pane1),
            new ContentPane("Panel2", pane2),
            new ContentPane("Panel3", pane3)
        };

        // create the left-hand side bar
        sideBar = new VBox(0);
        sideBar.setId("SideBar");
        sideBar.setPrefWidth(SIDEBAR_WIDTH);
        sideBar.setMinWidth(SIDEBAR_WIDTH);
        sideBar.setMaxWidth(SIDEBAR_WIDTH);
        sideBar.setStyle("-fx-background-color: #25282c");
        HBox.setHgrow(sideBar, Priority.NEVER);

        PersistentButtonToggleGroup toggleGroup = new PersistentButtonToggleGroup();
        for (int i=0; i < panes.length; i++) {
            final int index = i;
            final ContentPane contentPane = panes[i];
            final ToggleButton button = new ToggleButton(contentPane.getName());
            if (i==0) {
                Platform.runLater(new Runnable() {
                    @Override public void run() {
                        button.setSelected(true);
                        button.requestFocus();
                    }
                });
            }
            button.setMaxWidth(Double.MAX_VALUE);
            button.setAlignment(Pos.CENTER_LEFT);
            button.setTextAlignment(TextAlignment.LEFT);
            button.setToggleGroup(toggleGroup);
            button.setOnAction(new EventHandler<ActionEvent>() {
                @Override public void handle(ActionEvent t) {
                    switchcontentPane(contentPane, index);
                }
            });
            sideBar.getChildren().add(button);
        }

        // current Pane and sparePane are used for switching between panes
        currentPane = new StackPane();
        currentPane.getChildren().setAll(panes[0].getContent());

        sparePane = new StackPane();
        sparePane.setVisible(false);

        // contentPanePane is the right side of the scene where the relevant content is displayed
        StackPane contentPanePane = new StackPane();
        HBox.setHgrow(contentPanePane, Priority.ALWAYS);

        contentPanePane.getChildren().addAll(currentPane, sparePane);
        AnchorPane.setTopAnchor(currentPane, 0.0);
        AnchorPane.setRightAnchor(currentPane, 0.0);
        AnchorPane.setBottomAnchor(currentPane, 0.0);
        AnchorPane.setLeftAnchor(currentPane, 0.0);

        //create root node
        root = new HBox();
        root.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
        root.setPrefSize(1250, 844);

        root.getChildren().addAll(sideBar,contentPanePane);

        return SceneBuilder.create()
                .root(root)
                .build();
    }

    public void switchcontentPane(ContentPane newcontentPane, final int contentPaneIndex) {
        // check if existing animation running
        if (timeline != null) {
            nextPane = newcontentPane;
            nextPaneIndex = contentPaneIndex;
            timeline.setRate(4);
            return;
        } else {
            nextPane = null;
            nextPaneIndex = -1;
        }
        // load new content
        sparePane.getChildren().setAll(newcontentPane.getContent());
        sparePane.setCache(true);
        currentPane.setCache(true);
        // wait one pulse then animate
        Platform.runLater(new Runnable() {
            @Override public void run() {
                // animate switch
                if (contentPaneIndex > currentPaneIndex) { // animate from bottom
                    currentPaneIndex = contentPaneIndex;
                    sparePane.setTranslateY(root.getHeight());
                    sparePane.setVisible(true);
                    timeline = TimelineBuilder.create().keyFrames(
                                new KeyFrame(Duration.millis(0), 
                                    new KeyValue(currentPane.translateYProperty(),0,interpolator),
                                    new KeyValue(sparePane.translateYProperty(),root.getHeight(),interpolator)
                                ),
                                new KeyFrame(Duration.millis(800), 
                                    animationEndEventHandler,
                                    new KeyValue(currentPane.translateYProperty(),-root.getHeight(),interpolator),
                                    new KeyValue(sparePane.translateYProperty(),0,interpolator)
                                )
                            )
                            .build();
                    timeline.play();
                } else { // from top
                    currentPaneIndex = contentPaneIndex;
                    sparePane.setTranslateY(-root.getHeight());
                    sparePane.setVisible(true);
                    timeline = TimelineBuilder.create()
                            .keyFrames(
                                new KeyFrame(Duration.millis(0), 
                                    new KeyValue(currentPane.translateYProperty(),0,interpolator),
                                    new KeyValue(sparePane.translateYProperty(),-root.getHeight(),interpolator)
                                ),
                                new KeyFrame(Duration.millis(800), 
                                    animationEndEventHandler,
                                    new KeyValue(currentPane.translateYProperty(),root.getHeight(),interpolator),
                                    new KeyValue(sparePane.translateYProperty(),0,interpolator)
                                )
                            )
                            .build();
                    timeline.play();
                }
            }
        });
    }

    private EventHandler<ActionEvent> animationEndEventHandler = new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent t) {
            // switch panes
            StackPane temp = currentPane;
            currentPane = sparePane;
            sparePane = temp;
            // cleanup
            timeline = null;
            currentPane.setTranslateY(0);
            sparePane.setCache(false);
            currentPane.setCache(false);
            sparePane.setVisible(false);
            sparePane.getChildren().clear();
            // start any animations
            // check if we have a animation waiting
            if (nextPane != null) {
                switchcontentPane(nextPane, nextPaneIndex);
            }
        }
    };

    public static class PersistentButtonToggleGroup extends ToggleGroup {
        PersistentButtonToggleGroup() {
            super();
            getToggles().addListener(new ListChangeListener<Toggle>() {
                @Override
                public void onChanged(Change<? extends Toggle> c) {
                    while (c.next())
                        for (final Toggle addedToggle : c.getAddedSubList())
                            ((ToggleButton) addedToggle).addEventFilter(MouseEvent.MOUSE_RELEASED,
                                    new EventHandler<MouseEvent>() {
                                        @Override
                                        public void handle(MouseEvent mouseEvent) {
                                            if (addedToggle.equals(getSelectedToggle()))
                                                mouseEvent.consume();
                                        }
                                    });
                }
            });
        }
    }
}

这是包含右侧窗格内容的类

import javax.swing.JFrame;

import javafx.scene.Parent;

public class ContentPane {
    private String name;
    private Parent content;
    protected JFrame swingParent;

    public ContentPane(String name, Parent content) {
        this.name = name;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public Parent getContent() {
        return content;
    }
}

这是第一个内容窗格的 fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane fx:id="content" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Pane1Controller">
    <children>
        <VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
            <children>
                <Label maxWidth="1.7976931348623157E308" minHeight="-Infinity" prefHeight="48.0" style="-fx-font-size: 24; -fx-text-fill: #e6e6e6; -fx-background-color: #0072bc; -fx-label-padding: 0 0 0 12;" text="Pane 1" VBox.vgrow="NEVER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">

                </Label>
                <AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
                    <children>
                        <StackPane prefHeight="25.0" prefWidth="125.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
                            <children>
                                <Button fx:id="optionButton1" mnemonicParsing="false" text="Show a JOptionPane" />
                            </children>
                        </StackPane>
                    </children>
                </AnchorPane>
            </children>
        </VBox>
    </children>
</AnchorPane>

这是第一个窗格的控制器

import java.net.URL;
import java.util.ResourceBundle;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Pane1Controller implements Initializable {
    @FXML private Button optionButton1;

    private JFrame swingParent;

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {

        optionButton1.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                showJOptionPane();
            }
        });
    }

    private void showJOptionPane() {
        JOptionPane.showMessageDialog(swingParent, "So...now what?");
    }

}

第二个窗格 fxml...

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane fx:id="content" prefHeight="73.0" prefWidth="161.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Pane2Controller">
    <children>
        <VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
            <children>
                <Label maxWidth="1.7976931348623157E308" minHeight="-Infinity" prefHeight="48.0" style="-fx-font-size: 24; -fx-text-fill: #e6e6e6; -fx-background-color: #0072bc; -fx-label-padding: 0 0 0 12;" text="Pane 2" VBox.vgrow="NEVER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">

                </Label>
                <AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
                    <children>
                        <StackPane prefHeight="25.0" prefWidth="125.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
                            <children>
                                <Button fx:id="optionButton2" mnemonicParsing="false" text="Show another JOptionPane" />
                            </children>
                        </StackPane>
                    </children>
                </AnchorPane>
            </children>
        </VBox>
    </children>
</AnchorPane>

第二个窗格控制器...

import java.net.URL;
import java.util.ResourceBundle;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Pane2Controller implements Initializable {
    @FXML private Button optionButton2;

    private JFrame swingParent;

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {

        optionButton2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                showJOptionPane();
            }
        });
    }

    private void showJOptionPane() {
        JOptionPane.showMessageDialog(swingParent, "Here we go again.");
    }

}

第三窗格 fxml...

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane fx:id="content" prefHeight="73.0" prefWidth="161.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Pane3Controller">
    <children>
        <VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
            <children>
                <Label maxWidth="1.7976931348623157E308" minHeight="-Infinity" prefHeight="48.0" style="-fx-font-size: 24; -fx-text-fill: #e6e6e6; -fx-background-color: #0072bc; -fx-label-padding: 0 0 0 12;" text="Pane 3" VBox.vgrow="NEVER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">

                </Label>
                <AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
                    <children>
                        <StackPane prefHeight="25.0" prefWidth="125.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
                            <children>
                                <Button fx:id="optionButton3" mnemonicParsing="false" text="Show yet another JOptionPane" />
                            </children>
                        </StackPane>
                    </children>
                </AnchorPane>
            </children>
        </VBox>
    </children>
</AnchorPane>

第三个控制器...

import java.net.URL;
import java.util.ResourceBundle;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class Pane3Controller implements Initializable {
    @FXML private Button optionButton3;

    private JFrame swingParent;

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {

        optionButton3.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent e) {
                showJOptionPane();
            }
        });
    }

    private void showJOptionPane() {
        JOptionPane.showMessageDialog(swingParent, "Here we go again.");
    }

}

【问题讨论】:

    标签: java swing javafx-2


    【解决方案1】:

    JFXPanel 扩展了JComponent,因此您应该能够像使用任何 Swing 组件一样获得对 JFrame 的运行时引用。我假设您使用的是 getRootPane(...) 之类的东西。

    作为获取运行时引用的替代方法,您始终可以创建自己的扩展 JFXPanel 的类,并在自定义构造函数中传递对 JFrame 的引用。

    编辑

    如果您将SwingMain 类设为singleton,您应该能够轻松获得对其任何字段的引用。这是一个演示如何使用它。

    import java.awt.Color;
    import javafx.application.Platform;
    import javafx.embed.swing.JFXPanel;
    import javafx.scene.*;
    import javafx.scene.text.*;
    import javax.swing.*;
    
    public class SwingJFXCombo {
    
        /**
         * The control who starts everything.
         * This should have the references you need.
         * Uses the singleton pattern
         */
        public static class MainController{
    
            //Top level fields we may need access too
            JFXPanel jfxPanel;
            JPanel jp;
            JFrame frame;
    
            //Doing singleton stuff
            private static MainController instance;
            public static MainController getInstance(){
                if(instance == null){
                    instance = new MainController();
                }
                return instance;
            }
    
            private MainController(){
                jfxPanel = new JFXPanel();
                jp = new JPanel();
    
                jp.add(jfxPanel);
                jp.setVisible(true);
                jp.setBackground(Color.CYAN);
    
                //Setup to display swing stuff
                frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                frame.add(jp);
                frame.pack();
            }
    
            public static void initialize(){
                getInstance();
    
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        MainController mc = getInstance();
                        Scene scene = mc.initScene();
                        mc.jfxPanel.setScene(scene);
                    }
                });
            }
    
            private Scene initScene(){
                Group  root  =  new  Group();
                Scene  scene  =  new  Scene(root, javafx.scene.paint.Color.ALICEBLUE);
                Text  text  =  new  Text(40,100,"JavaFX Scene");
                text.setFont(new Font(25));
                root.getChildren().add(text);
                return (scene);
            }
        }
    
        /**
         * Another random class for demonstration purposes
         * This would be similar to your FX Controllers
         */
        public static class RandomController{
            public void printFrameColor(){
                //Now from anywhere, I can get any of the top level items by calling the singleton
                System.out.println(MainController.getInstance().frame.getBackground());
            }
        }
    
        public static void main(String[] args){
            MainController.initialize();
            new RandomController().printFrameColor();
    
    
        }
    }
    

    【讨论】:

    • 感谢您的帮助!虽然,它比这更复杂一些。实际上,为 JFXPanel 创建场景的类具有对传入构造函数的 JFrame 的引用。我遇到的问题是获得对右侧动态内容的引用。它们都从 FXMLLoader 实例化为父级,我找不到传递引用的好方法(我尝试过创建接口并传递构造函数参数)。由于它们是通用创建的,因此界面对我没有好处。
    • @RonSiven 也许如果您能提供您遇到的问题的简化版本,我可以提供更好的帮助。我可以编造一些东西,但它可能不会比我最初的尝试更适合你的情况。
    • 我在原始问题中添加了一个工作示例。我希望它有效地传达了这一点。再次感谢!
    • @RonSiven 我已经更新了我的答案。这应该让你走上正确的轨道。您的示例有点长(9 个文件),所以我没有加载它,但是看到您正在做的事情的要点确实有帮助。
    • 我终于回到了这个问题,将父级实现为单例确实解决了这个问题。在这种情况下,它还具有使应用程序更健壮的副作用。一次应该只有其中一个呈现给用户。所以,再次感谢!
    猜你喜欢
    • 2015-03-19
    • 2011-10-09
    • 2017-04-23
    • 2023-03-29
    • 1970-01-01
    • 2012-11-21
    • 2013-12-23
    • 2013-05-27
    • 2020-06-14
    相关资源
    最近更新 更多