我有一个完善的Swing应用程序,正在使用JFXPanel部分升级到JavaFX。带有JFXPanel的JFrame从另一个类中获取一个场景,该场景在代码(不是FXML)中创建一个根节点,该根节点是HBox,其左侧边栏非常简单,具有动态生成的链接,按钮和内容,右侧边栏是根据用户在左侧按的按钮动态填充内容。右侧内容由几组FXML和控制器类构建。
在每个这些右侧内容类中,我都必须调出一些标准的Swing JDialogs或JOptionPane,它们在构造函数中接受父级(JFrame或JDialog)以进行屏幕放置和模态显示。 (这些JDialogs和JOptionPanes来自我们多年来编写的通用组件库)
我在运行时是否有办法获取对包含JFXPanel的JFrame的引用,以便可以使用适当的方式实例化它们?
**编辑**
模态实际上有效
我现在真正想要做的是将对话框置于父项上方。当然,我确信这对将来对我有许多其他原因都是有用的。
这是一个缩写版本。我已经能够运行它,所以它应该为您工作。
如果这有点混乱,我深表歉意。
多谢您的协助!
这是主要的类:
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.");
}
}
最佳答案
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();
}
}