I have a problem with fxml controller communication.
I have App class which is a controller:
package org.example.gui;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import org.example.Game;
import org.example.Main;
import java.io.IOException;
public class App extends Application {
private Game game;
private MapVisualiser mapV;
#FXML
private GridPane mapGridPane;
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/main-scene.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 1530, 790);
stage.setResizable(true);
stage.setMaximized(true);
stage.setTitle("GymSnake");
stage.setScene(scene);
this.game = new Game();
this.mapV = new MapVisualiser(game.getMap(),mapGridPane);
stage.show();
}
}
and a fxml file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.GridPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.gui.App">
<GridPane fx:id="mapGridPane" alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" nodeOrientation="LEFT_TO_RIGHT">
</GridPane>
</AnchorPane>
Also the MapVisualiser code to make sure everything is working fine
public MapVisualiser(Map map, GridPane gridPane) {
this.map = map;
this.gridPane = gridPane;
calculateColumnsAndRowsSize();
}
private void calculateColumnsAndRowsSize() {
int squareHeight = (int) Math.ceil(1.0 * Configuration.GRID_HEIGHT / map.getN());
int squareWidth = (int) Math.ceil(1.0 * Configuration.GRID_WIDTH / map.getN());
this.sideLengthOfSquarePx = Math.max(40, Math.max(squareWidth, squareHeight));
gridPane.getColumnConstraints().add(new ColumnConstraints(sideLengthOfSquarePx));
gridPane.getRowConstraints().add(new RowConstraints(sideLengthOfSquarePx));
}
I don't understand why my grid pane returns null
Caused by: java.lang.NullPointerException: Cannot invoke "javafx.scene.layout.GridPane.getColumnConstraints()" because "this.gridPane" is null
Please help
I'm working on a project and I need to display the information of a Medicine object from a ListView to another Scene.
The user would select a Medicine from the ListView and press the Button to see it's details in the next Scene that would be displayed by Labels. The problem now is, I have transferred the info, and the text property of the Label has changed (observed through println and debugging), but the Label just won't display the changed text.
this is the main
package app;
import app.data.MedicineData;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import org.w3c.dom.events.Event;
import java.io.IOException;
public class Pharmachine extends Application {
public static final double WIDTH = 480;
public static final double HEIGHT = 720;
#Override
public void start(Stage stage) throws Exception{
Parent homePage = FXMLLoader.load(getClass().getResource("homepage.fxml"));
Scene homePageScene = newDefaultScene(homePage);
stage.setResizable(false);
stage.setScene(homePageScene);
stage.setTitle("Pharmachine");
stage.show();
}
#Override
public void init(){
MedicineData.getInstance().loadData();
}
public static Scene newDefaultScene(Parent parent){
Scene scene = new Scene(parent, WIDTH, HEIGHT);
scene.addEventHandler(MouseEvent.MOUSE_CLICKED,
(mouseEvent) -> {
Node targetNode = scene.getFocusOwner();
if(targetNode != null && targetNode.isFocused()) {
if(targetNode.getParent() != null)
targetNode.getParent().requestFocus();
mouseEvent.consume();
}
}
);
return scene;
}
public static void navigateTo(ActionEvent actionEvent, String filename){
Stage mainWindow = (Stage) ((Node) actionEvent.getSource()).getScene().getWindow();
Scene currentScene = ((Node) actionEvent.getSource()).getScene();
try {
currentScene.setRoot(FXMLLoader.load(Pharmachine.class.getResource(filename)));
mainWindow.setScene(currentScene);
} catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){ launch(args); }
}
this is the fxml for the list's page
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="app.ListpageController" fx:id="pageRoot"
stylesheets="#styles/main.css">
<left>
<GridPane fx:id="listArea" alignment="CENTER">
<ListView fx:id="medicineList"
GridPane.rowIndex="1"/>
</GridPane>
</left>
<center>
<GridPane fx:id="buttonsArea" alignment="CENTER"
hgap="10" vgap="10">
<Button text="Back"
onAction="#displayHomePage"
GridPane.halignment="CENTER"/>
<Button text="Details"
onAction="#displayDetailsPage"
GridPane.rowIndex="1"
GridPane.halignment="CENTER"/>
</GridPane>
</center>
</BorderPane>
here is the controller for the list's page
package app;
import app.data.Medicine;
import app.data.MedicineData;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.util.Callback;
import java.io.IOException;
public class ListpageController {
#FXML
private BorderPane pageRoot;
#FXML
private GridPane listArea;
#FXML
private GridPane buttonsArea;
#FXML
private ListView<Medicine> medicineList;
#FXML public void initialize(){
listArea.setMaxWidth(Pharmachine.WIDTH * 3/5);
medicineList.setMinWidth(listArea.getMaxWidth());
medicineList.setMinHeight(Pharmachine.HEIGHT);
medicineList.setItems(MedicineData.getInstance().getMedicines());
medicineList.setCellFactory(new Callback<>() {
#Override
public ListCell<Medicine> call(ListView<Medicine> medicineListView) {
ListCell<Medicine> listCell = new ListCell<>(){
#Override
public void updateItem(Medicine medicine, boolean empty){
super.updateItem(medicine, empty);
if(!empty){
HBox medName = new HBox(new Label(medicine.getName()));
HBox.setHgrow(medName, Priority.ALWAYS);
HBox medPrice = new HBox(new Label(String.format("RM%.2f", medicine.getPrice())));
HBox medLabel = new HBox(medName, medPrice);
setGraphic(medLabel);
}
}
};
listCell.setOnMouseClicked(
(mouseEvent) -> {
if(mouseEvent.getClickCount() == 2){
medicineList.getSelectionModel().clearSelection();
}
}
);
return listCell;
}
});
}
#FXML private void displayHomePage(ActionEvent actionEvent){
Pharmachine.navigateTo(actionEvent, "homepage.fxml");
}
#FXML private void displayDetailsPage(ActionEvent actionEvent){
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("detailspage.fxml"));
try {
loader.load();
} catch(IOException e){
e.printStackTrace();
}
DetailspageController controller = loader.getController();
Medicine selectedMedicine = medicineList.getSelectionModel().getSelectedItem();
if(selectedMedicine != null) {
controller.setInfo(selectedMedicine);
Pharmachine.navigateTo(actionEvent, "detailspage.fxml");
}
}
}
this is the fxml for the next scene (just simple for now)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.text.Text?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:id="pageRoot" fx:controller="app.DetailspageController"
stylesheets="#styles/main.css">
<center>
<GridPane fx:id="detailsArea"
alignment="CENTER" hgap="10" vgap="10">
<Label text="Details"/>
<Text text="Name: "
GridPane.rowIndex="1"/>
<Label fx:id="medNameLabel" text="~"
GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Text text="Price: "
GridPane.rowIndex="2"/>
<Label fx:id="medPriceLabel" text="~"
GridPane.rowIndex="2" GridPane.columnIndex="1"/>
</GridPane>
</center>
</BorderPane>
and this is the scene's controller
package app;
import app.data.Medicine;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class DetailspageController implements Initializable {
private Medicine selectedMedicine;
#FXML
private BorderPane pageRoot;
#FXML
private GridPane detailsArea;
#FXML
private Label medNameLabel;
#FXML
private Label medPriceLabel;
#Override
public void initialize(URL url, ResourceBundle rb){
}
public void setInfo(Medicine medicine){
selectedMedicine = medicine;
medNameLabel.setText(selectedMedicine.getName());
medPriceLabel.setText(String.format("RM%.2f", selectedMedicine.getPrice()));
System.out.println(medNameLabel.textProperty());
System.out.println(medNameLabel.getText());
medNameLabel.setStyle("-fx-background-color: red;");
}
}
The help would mean a lot. Thank you :]
In your displayDetailsPage method, you load a scene from detailspage.fxml, and you update its labels by calling the setInfo method of the controller.
Then, you call Pharmachine.navigateTo, which loads detailspage.xml again and replaces the scene root with the newly loaded root. You updated the text of the labels in the first details page, but the second details page is brand new, so it doesn’t have those changes.
The second argument in the navigateTo method should be of type Parent rather than a String. navigateTo should not attempt to load any .fxml file; let that be the caller’s responsibility.
Side note: When a list has multiple attributes for each data item, you should probably use a TableView rather than a ListView.
Listview cells loading blank instead of custom cell
I've tried rebuilding fxml file, using debug tools, none of these have helped.I've looked extensively on the net but can only find guides for older versions of javafx listview.
Before some tells me that the updateItem code is wrong, please just help me get it working first, then at least it'll be somewhere to start from for optimizing it
package Test;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Pane;
import org.controlsfx.glyphfont.Glyph;
/**
* FXML Controller class
*
* #author james
*/
public class ListCellController2 extends ListCell<Student>
{
FXMLLoader loader;
#FXML
private Glyph attendenceSymbolGlyph;
#FXML
private Label studentDetailsLabel;
#FXML
private Pane entryPane;
#Override
protected void updateItem(Student student, boolean empty)
{
super.updateItem(student, empty);
if (empty || student == null)
{
setText(null);
setGraphic(null);
}
else
{
loader = new FXMLLoader(getClass().getResource("ListCell2.fxml"));
try
{
loader.load();
studentDetailsLabel.setText(student.toString());
setGraphic(entryPane);
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import org.controlsfx.glyphfont.Glyph?>
<Pane fx:id="entryPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="95.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Test.ListCellController2">
<children>
<Glyph id="attendenceSymbolGlyph" fx:id="attendenceSymbolGlyph" layoutX="5.0" layoutY="8.0" prefHeight="80.0" prefWidth="87.0" />
<Label id="studentDetailsLabel" fx:id="studentDetailsLabel" layoutX="113.0" layoutY="5.0" prefHeight="88.0" prefWidth="482.0" text="Label" />
</children>
</Pane>
package Test;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
/**
* FXML Controller class
*
* #author james
*/
public class MainController implements Initializable {
#FXML
public ListView<Student> myListView;
public ObservableList<Student> studentList;
public MainController()
{
studentList = FXCollections.observableArrayList();
studentList.add(new Student("Jimmy", "u0764987", "ef937b3"));
studentList.add(new Student("John", "u0762809", "543jh32"));
}
public void setupListView()
{
try
{
myListView.setItems((ObservableList)studentList);
myListView.setCellFactory(new Callback<ListView<Student>, ListCell<Student>>()
{
#Override
public ListCell<Student> call(ListView<Student> p)
{
return new ListCellController();
}
});
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL location, ResourceBundle resources)
{
if (!studentList.isEmpty())
{
setupListView();
}
else
{
System.out.println("student list is empty");
}
}
}
My expected result would be that it loads my data into the custom cell then displays it, what it actually does is is give me a blank cell
It seems you created two instances of your ListCellController2 class.
you create the first instance in your list view cell factory
the second instance is created when you load the FXML as the controller class is given inside the FXML
I also suggest you only load the list cell component once when your cell is created, instead of every time a new value is shown.
The controller is created by the cell factory and the instance is used by the FXML-loader. This way, the fields annotated with #FXML get initialized correctly.
Here is my code:
MainController.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
public class MainController implements Initializable {
#FXML
public ListView<Student> myListView;
public ObservableList<Student> studentList;
public MainController() {
studentList = FXCollections.observableArrayList();
studentList.add(new Student("Jimmy", "u0764987", "ef937b3"));
studentList.add(new Student("John", "u0762809", "543jh32"));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
if (!studentList.isEmpty()) {
setupListView();
} else {
System.out.println("student list is empty");
}
}
private void setupListView() {
myListView.setItems(studentList);
myListView.setCellFactory((listView) -> new ListCellController2());
}
}
ListCellController2
import java.io.IOException;
import org.controlsfx.glyphfont.Glyph;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Pane;
public class ListCellController2 extends ListCell<Student> {
#FXML
private Glyph attendenceSymbolGlyph;
#FXML
private Label studentDetailsLabel;
#FXML
private Pane entryPane;
public ListCellController2() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setController(this);
loader.setLocation(getClass().getResource("ListCell2.fxml"));
loader.load();
} catch (IOException e) {
throw new RuntimeException("Creating UI component failed", e);
}
}
#Override
protected void updateItem(Student student, boolean empty) {
super.updateItem(student, empty);
if (empty || student == null) {
setText(null);
setGraphic(null);
} else {
studentDetailsLabel.setText(student.toString());
setGraphic(entryPane);
}
}
}
ListCell2.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import org.controlsfx.glyphfont.Glyph?>
<Pane fx:id="entryPane" prefHeight="95.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Glyph id="attendenceSymbolGlyph" fx:id="attendenceSymbolGlyph" layoutX="5.0" layoutY="8.0" prefHeight="80.0" prefWidth="87.0" />
<Label id="studentDetailsLabel" fx:id="studentDetailsLabel" layoutX="113.0" layoutY="5.0" prefHeight="88.0" prefWidth="482.0" text="Label" />
</children>
</Pane>
I am getting this error
the controller "classname" has no event slot "methodname"
if i add "/" before application in fx:controller="application.MainController">
this error goes but creates
class failed to load
/Users/myname/Documents/workspace/Javafx%20tesr/bin/application/Main.fxml:7
here's the xml file
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="450" prefWidth="500" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<Button fx:id="genratebutton" layoutX="160.0" layoutY="225.0" mnemonicParsing="false" onAction="#generaterandom" prefHeight="27.0" prefWidth="180.0" text="generate random" />
<Label fx:id="myMessage" layoutX="138.0" layoutY="104.0" prefHeight="89.0" prefWidth="225.0" />
</children>
</AnchorPane>
(sorry new to GUI only know basics of swing)
this is main class
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root=FXMLLoader.load(getClass().getClassLoader().getResource("/Main.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
catch(Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public static void main(String[] args) {
launch(args);
}
}
this is another class whose method i want to invoke
package application;
import java.util.Random;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class MainController {
#FXML
private Label myMessage;
void generaterandom (ActionEvent event){
Random rand=new Random();
int myrand=rand.nextInt(50)+1;
myMessage.setText(Integer.toString(myrand));
System.out.println(Integer.toString(myrand));
}
}
Since the controller method is not public, you need to annotate it #FXML:
public class MainController {
#FXML
private Label myMessage;
#FXML
void generaterandom (ActionEvent event){
Random rand=new Random();
int myrand=rand.nextInt(50)+1;
myMessage.setText(Integer.toString(myrand));
System.out.println(Integer.toString(myrand));
}
}
I have a well established Swing application that I'm partially upgrading to JavaFX using JFXPanel. The JFrame with the JFXPanel gets a scene from another class which creates a root node in code (not FXML) that is an HBox with a pretty simple left-hand sidebar with dynamically generated links and buttons and stuff and a right-hand side that is dynamically filled in with content based on the button a user presses on the left side. The right-side content is built with several sets of FXML and controller classes.
Within each of these right-side content classes, I have to bring up some standard Swing JDialogs or JOptionPane's that accept a parent (JFrame or JDialog) in the constructor for screen placement and modality. (These JDialogs and JOptionPanes come from a library of common components that we have written over the years)
Is there a way at runtime for me to get a reference to the JFrame containing the JFXPanel so I can instantiate them with proper modality?
** EDIT **
Modality actually works
What I really want to do now is center the dialog over the parent. Of course, I'm sure that this will be useful for a myriad of other reasons for me in the future.
Here is an abbreviated version. I have been able to run it, so it should work for you.
I apologize if this is a bit messy :-S
Thanks a ton for your assistance!
This is the main class:
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
}
}
});
}
}
This is the class that provides the scene for the 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();
}
});
}
});
}
}
}
This is the class that will contain the content for the right side panes
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;
}
}
This is the fxml for the first content pane
<?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>
This is the controller for the first pane
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?");
}
}
Second pane 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>
Second pane controller...
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.");
}
}
Third pane 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>
3rd controller...
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 extends JComponent, therefore you should be able to get a runtime reference to the JFrame the same way you would using any Swing component. I'm assuming you're using something like getRootPane(...).
As an alternative to getting a runtime reference, you could always create your own class extending JFXPanel and pass in a reference to the JFrame in your custom constructor.
Edit
If you make your SwingMain class into a singleton, you should be able to easily get a references to any of it's fields. Here's a demo of how you could use it.
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();
}
}