JavaFX 2.2 JFXPanel in Swing application showing Swing modal dialogs - java

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

Related

Label not displaying text even after change (JavaFX)

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.

NullPointerException Setting ComboBox From FXML [duplicate]

I'm really struggling to understand JavaFX controllers, my aim is to write to a TextArea to act as a log.
My code is below, but I want to be able to change values ETC from another class that I can call when needed. I have tried to create a controller class that extents Initializable but i cant get it to work. Could some one steer me in the correct direction?
I want to move the #FXML code at the bottom to another class and it update the Scene.
package application;
import javafx.event.ActionEvent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
Scene scene = new Scene(root,504,325);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public Thread thread = new Thread(new webimporter());
#FXML
public Label runningLabel;
#FXML
public TextArea txtArea;
#FXML
void runClick(ActionEvent event) throws IOException{
changeLabelValue("Importer running...");
thread.start();
}
#FXML
protected void stopClick(ActionEvent event){
changeLabelValue("Importer stopped...");
thread.interrupt();
}
#FXML
void changeLabelValue(String newText){
runningLabel.setText(newText);
}
void changeTextAreaValue(String newText1){
txtArea.setText(newText1);
}
}
Don't make the Application class a controller. It's a sin. There are other questions and answers which address this, but my search skills cannot find them at this time.
The reason it is a sin is:
You are only supposed to have one Application instance, and, by default, the loader will make a new instance, so you end up with two application objects.
Referencing the member objects is confusing, because the original launched application doesn't have the #FXML injected fields, but the loader created application instance does have #FXML inject fields.
Also, unrelated advice: Don't start trying to write multi-threaded code until you have the application at least working to the extent where it displays your UI.
A multi-threaded logger for JavaFX is in the answer to Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks, though unfortunately it is not straight-forward in its implementation and comes with little documentation.
textlogger/Root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="400.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="textlogger.ImportController">
<children>
<HBox alignment="BASELINE_LEFT" minHeight="-Infinity" minWidth="-Infinity" spacing="10.0">
<children>
<Button mnemonicParsing="false" onAction="#run" text="Run" />
<Button mnemonicParsing="false" onAction="#stop" text="Stop" />
<Label fx:id="runningLabel" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</HBox>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
textlogger.ImportController.java
package textlogger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
public class ImportController {
#FXML
private Label runningLabel;
#FXML
private TextArea textArea;
private WebImporter importer;
#FXML
void run(ActionEvent event) throws IOException {
changeLabelValue("Importer running...");
if (importer == null) {
importer = new WebImporter(textArea);
Thread thread = new Thread(
importer
);
thread.setDaemon(true);
thread.start();
}
}
#FXML
void stop(ActionEvent event){
changeLabelValue("Importer stopped...");
if (importer != null) {
importer.cancel();
importer = null;
}
}
private void changeLabelValue(String newText){
runningLabel.setText(newText);
}
}
textlogger.WebImporter.java
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.TextArea;
import java.time.LocalTime;
public class WebImporter extends Task<Void> {
private final TextArea textArea;
public WebImporter(TextArea textArea) {
this.textArea = textArea;
}
#Override
protected Void call() throws Exception {
try {
while (!isCancelled()) {
Thread.sleep(500);
Platform.runLater(
() -> textArea.setText(
textArea.getText() + LocalTime.now() + "\n"
)
);
}
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
}
textlogger.TextLoggingSample.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TextLoggingSample extends Application {
#Override
public void start(Stage stage) {
try {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(
getClass().getResourceAsStream(
"Root.fxml"
)
);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

JavaFx tableview RuntimeException

I am just trying to build a simple Gui with SceneBuilder and JavaFx however I can't figure out why I can't populate my TableView, it just stays empty even after inserting simple Testobjects. Here is the main Class and the Goal class.
package application;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ResourceBundle;
import Objects.Goal;
import UtilityClasses.GoalManager;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
public class Main extends Application implements Initializable {
private Stage primaryStage;
private AnchorPane mainLayout;
public static ObservableList<Goal> goalsData = FXCollections.observableArrayList();
#FXML
static TableView<Goal> goalTable = new TableView<Goal>();
#FXML
static TableColumn<Goal, String> goalsColumn = new TableColumn<>();
#FXML
static TableColumn<Goal, String> statusColumn = new TableColumn<>();
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("MainWindow");
try {
showMainView();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
try {
setDefaultSettings();
launch(args);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void showMainView() throws IOException {
SettingControlls mainController = new SettingControlls();
FXMLLoader loader = new FXMLLoader();
loader.setController(mainController);
loader.setLocation(Main.class.getResource("mainFXML.fxml"));
mainLayout = loader.load();
Scene scene = new Scene(mainLayout);
primaryStage.setScene(scene);
primaryStage.show();
}
private static void setDefaultSettings() throws IOException {
File f = new File("Goals.txt");
if(!f.exists()) {
OutputStream os = new FileOutputStream("Goals.txt");
Writer w = new OutputStreamWriter(os);
w.close();
}
}
private static ObservableList<Goal> getObservableList() throws FileNotFoundException, IOException {
ObservableList<Goal> ol = FXCollections.observableArrayList(new Goal("testGoal"));
return ol;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
goalsData.add(new Goal("test"));
System.out.println(goalsData.get(0).getGoal());
goalsColumn.setCellValueFactory(new PropertyValueFactory<Goal, String>("goal"));
statusColumn.setCellValueFactory(new PropertyValueFactory<Goal, String>("status"));
goalTable.setItems(goalsData);
}
}
Here is the Goal class:
package Objects;
import javafx.beans.property.SimpleStringProperty;
public class Goal {
private SimpleStringProperty goal;
private SimpleStringProperty status;
public Goal(String goal) {
this.goal = new SimpleStringProperty(goal);
this.status = new SimpleStringProperty("ongoing");
}
public Goal(String goal, String status) {
this.goal = new SimpleStringProperty(goal);
this.status = new SimpleStringProperty(status);
}
public String getGoal() {
return this.goal.get();
}
public String getStatus() {
return this.status.get();
}
public void setGoal(String newGoal) {
this.goal = new SimpleStringProperty(newGoal);
}
public void setStatus(String newStatus) {
this.status = new SimpleStringProperty(newStatus);
}
}
Because the TableView and the columns get declared in my fxml file it seemed weird to me to generate them with new however if I don't do that I get an Nullpointerexception.
Edit: Added the fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane prefHeight="406.0" prefWidth="721.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MenuBar layoutX="-7.0" layoutY="14.0" prefHeight="25.0" prefWidth="733.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<menus>
<Menu mnemonicParsing="false" onAction="#GoalsClicked" style="-fx-font-size: 23;" text="Goals" />
<Menu disable="true" mnemonicParsing="false" style="-fx-font-size: 23;" text="Matchups" />
</menus>
</MenuBar>
<Text layoutX="14.0" layoutY="87.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Your current Goals:">
<font>
<Font size="24.0" />
</font>
</Text>
<TableView fx:id="goalTable" layoutY="103.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0">
<columns>
<TableColumn fx:id="goalsColumn" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="625.0" text="Goals" />
<TableColumn fx:id="statusColumn" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="72.0" text="Status" />
</columns>
<contextMenu>
<ContextMenu>
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
<MenuItem mnemonicParsing="false" text="Set status" />
</items>
</ContextMenu>
</contextMenu>
</TableView>
<Button layoutX="619.0" layoutY="315.0" mnemonicParsing="false" onAction="#addGoal" text="+" />
<Button layoutX="655.0" layoutY="315.0" mnemonicParsing="false" onAction="#removeGoal" text="-" />
</children>
</AnchorPane>
You have many problems there :
It is not recommended to use your Main class as a Controller class.
Your Nodes used in .fxml are never static since they belong to the instance not to the class so remove them.
Don't instantiate the Nodes defined in the .fxml file. There is nothing to do with new. The FXMLLoader does the work for you.
So rewrite the following part:
#FXML
static TableView<Goal> goalTable = new TableView<Goal>();
#FXML
static TableColumn<Goal, String> goalsColumn = new TableColumn<>();
#FXML
static TableColumn<Goal, String> statusColumn = new TableColumn<>();
I would also suggest splitting the Main class into a Main and a Controller class.
In Main you should just load the file, and in the Controller do the UI related stuff.
You can split the following way:
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
AnchorPane pane = loader.load();
primaryStage.setScene(new Scene(pane, 400, 400));
primaryStage.show();
}
}
Controller:
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
and the .fxml
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.dummy.Controller">
</AnchorPane>
Then you can complete these classes respecting those two rules I mentioned at 2. and 3.

Are controller nodes always null inside setOnCloseRequest? [duplicate]

I'm really struggling to understand JavaFX controllers, my aim is to write to a TextArea to act as a log.
My code is below, but I want to be able to change values ETC from another class that I can call when needed. I have tried to create a controller class that extents Initializable but i cant get it to work. Could some one steer me in the correct direction?
I want to move the #FXML code at the bottom to another class and it update the Scene.
package application;
import javafx.event.ActionEvent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("Root.fxml"));
Scene scene = new Scene(root,504,325);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
public Thread thread = new Thread(new webimporter());
#FXML
public Label runningLabel;
#FXML
public TextArea txtArea;
#FXML
void runClick(ActionEvent event) throws IOException{
changeLabelValue("Importer running...");
thread.start();
}
#FXML
protected void stopClick(ActionEvent event){
changeLabelValue("Importer stopped...");
thread.interrupt();
}
#FXML
void changeLabelValue(String newText){
runningLabel.setText(newText);
}
void changeTextAreaValue(String newText1){
txtArea.setText(newText1);
}
}
Don't make the Application class a controller. It's a sin. There are other questions and answers which address this, but my search skills cannot find them at this time.
The reason it is a sin is:
You are only supposed to have one Application instance, and, by default, the loader will make a new instance, so you end up with two application objects.
Referencing the member objects is confusing, because the original launched application doesn't have the #FXML injected fields, but the loader created application instance does have #FXML inject fields.
Also, unrelated advice: Don't start trying to write multi-threaded code until you have the application at least working to the extent where it displays your UI.
A multi-threaded logger for JavaFX is in the answer to Most efficient way to log messages to JavaFX TextArea via threads with simple custom logging frameworks, though unfortunately it is not straight-forward in its implementation and comes with little documentation.
textlogger/Root.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="400.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="textlogger.ImportController">
<children>
<HBox alignment="BASELINE_LEFT" minHeight="-Infinity" minWidth="-Infinity" spacing="10.0">
<children>
<Button mnemonicParsing="false" onAction="#run" text="Run" />
<Button mnemonicParsing="false" onAction="#stop" text="Stop" />
<Label fx:id="runningLabel" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</HBox>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
textlogger.ImportController.java
package textlogger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import java.io.IOException;
public class ImportController {
#FXML
private Label runningLabel;
#FXML
private TextArea textArea;
private WebImporter importer;
#FXML
void run(ActionEvent event) throws IOException {
changeLabelValue("Importer running...");
if (importer == null) {
importer = new WebImporter(textArea);
Thread thread = new Thread(
importer
);
thread.setDaemon(true);
thread.start();
}
}
#FXML
void stop(ActionEvent event){
changeLabelValue("Importer stopped...");
if (importer != null) {
importer.cancel();
importer = null;
}
}
private void changeLabelValue(String newText){
runningLabel.setText(newText);
}
}
textlogger.WebImporter.java
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.TextArea;
import java.time.LocalTime;
public class WebImporter extends Task<Void> {
private final TextArea textArea;
public WebImporter(TextArea textArea) {
this.textArea = textArea;
}
#Override
protected Void call() throws Exception {
try {
while (!isCancelled()) {
Thread.sleep(500);
Platform.runLater(
() -> textArea.setText(
textArea.getText() + LocalTime.now() + "\n"
)
);
}
} catch (InterruptedException e) {
Thread.interrupted();
}
return null;
}
}
textlogger.TextLoggingSample.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TextLoggingSample extends Application {
#Override
public void start(Stage stage) {
try {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(
getClass().getResourceAsStream(
"Root.fxml"
)
);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

Updating label in JavaFX [duplicate]

This question already has answers here:
Javafx - Can application class be the controller class
(2 answers)
Closed 7 years ago.
How can I update the text of the label currentPlayerFileLabel from the if statement, so it gets the path from the file (if it is there); otherwise gets the default String?
Code as of now:
package sample;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import sample.controllers.Util;
import sample.model.Config;
import java.io.File;
import java.net.URL;
import java.security.spec.ECField;
import java.util.ResourceBundle;
public class Main extends Application implements Initializable {
private Stage primaryStage = new Stage();
private BorderPane rootLayout;
private AnchorPane anchorPanePlayer;
private BorderPane borderPaneGame;
private Config config = new Config();
private StringProperty isPlayerFileThere = new SimpleStringProperty("No playerfile was fund! Add new one in \"File\"");
#FXML
private Button playersButton;
#FXML
private Button gamesButton;
#FXML
private Button quitButton;
#FXML
private Label currentPlayerFileLabel = new Label();
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Main window");
initLayout();
initConfig();
}
//----------- MY QUESTION IS ABOUT THIS METHODE -------------------------------
public void initConfig() {
File configFile = new File(System.getProperty("user.dir") + "/Config/config.txt");
if (configFile.exists()) { // Returns true as of now, so the "true" statement of the if statement will be called
config = Util.initConfigFile();
isPlayerFileThere.setValue(config.getPlayerFileLocation().toString());
currentPlayerFileLabel.setText(getIsPlayerFileThere());
} else {
currentPlayerFileLabel.setText(getIsPlayerFileThere());
}
}
//----------- MY QUESTION IS ABOUT THIS METHODE -------------------------------
public void initLayout() {
try {
//Load root layout from fxml
FXMLLoader loader = new FXMLLoader(); //Makes a new FXMLLoader
loader.setLocation(Main.class.getResource("view/mainView.fxml")); //sets the location of the main fxml file
rootLayout = loader.load(); //Loads the anchorpane from the loader, (AnchorPane) is redundent.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.getStackTrace();
}
}
public void initPlayerLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/playerEdit.fxml")); // Gets the new layout.
anchorPanePlayer = loader.load(); //Loades the new layout
Scene playerScene = new Scene(anchorPanePlayer); //adds a new scene with the loaded layout
Stage prevStage = (Stage) playersButton.getScene().getWindow(); //Get the stage from where we come from.
prevStage.close(); //Closes the prev stage
primaryStage.setScene(playerScene); //Sets new stage with the new layout
primaryStage.show(); //Shows new stage
} catch (Exception e) {
e.printStackTrace();
}
}
public void initGameLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/editGame.fxml"));
borderPaneGame = loader.load();
Scene gameScene = new Scene(borderPaneGame);
Stage prevStage = (Stage) gamesButton.getScene().getWindow();
prevStage.close();
primaryStage.setScene(gameScene);
primaryStage.show();
} catch (Exception e) {
e.getStackTrace();
}
}
public void quitProgram() {
Stage stageToQuit = (Stage) quitButton.getScene().getWindow();
stageToQuit.close();
}
public Stage getPrimaryStage() {
return primaryStage;
}
public AnchorPane getBorderPanePlayer() {
return anchorPanePlayer;
}
public Config getConfig() {
return config;
}
public void addPlayerFile() {
config.setPlayerFileLocation(Util.addPlayerFile());
}
public String getIsPlayerFileThere() {
return isPlayerFileThere.get();
}
public StringProperty isPlayerFileThereProperty() {
return isPlayerFileThere;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
currentPlayerFileLabel.setText(getIsPlayerFileThere());
}
public static void main(String[] args) {
launch(args);
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Main">
<bottom>
<HBox prefHeight="100.0" prefWidth="200.0" spacing="40.0" BorderPane.alignment="CENTER">
<children>
<Region HBox.hgrow="ALWAYS" />
<Button fx:id="playersButton" mnemonicParsing="false" onAction="#initPlayerLayout" text="Players" />
<Button fx:id="gamesButton" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#initGameLayout" text="Games" />
<Button fx:id="quitButton" layoutX="69.0" layoutY="10.0" mnemonicParsing="false" onAction="#quitProgram" text="Quit" />
<Region layoutX="10.0" layoutY="10.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>
</bottom>
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="addPlayerFileMenu" mnemonicParsing="false" onAction="#addPlayerFile" text="Add new player file" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<HBox prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER_RIGHT">
<children>
<VBox alignment="CENTER" HBox.hgrow="ALWAYS">
<children>
<Label text="Welcom to the main menu!" />
<Label fx:id="currentPlayerFileLabel" text="Label" />
</children>
</VBox>
</children>
</HBox>
</center>
</BorderPane>
UPDATE Have now worked out how to do it, with the help from the comments on this question. To others with the same problem, here is the code that I got to work:
Main class:
package sample;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import sample.controllers.MainController;
import sample.controllers.Util;
import sample.model.Config;
import java.io.File;
import java.net.URL;
import java.security.spec.ECField;
import java.util.ResourceBundle;
public class Main extends Application {
MainController mainController = new MainController();
Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("view/mainView.fxml"));
loader.setController(mainController);
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
The controller for the layout of the main window and the part that is interesting:
...
private Stage primaryStage = new Stage();
private AnchorPane anchorPanePlayer;
private BorderPane borderPaneGame;
private Config config = new Config();
private StringProperty isPlayerFileThere = new SimpleStringProperty("No playerfile was fund! Add new one in \"File\"");
#FXML
private Button playersButton;
#FXML
private Button gamesButton;
#FXML
private Button quitButton;
#FXML
private Label currentPlayerFileLabel;
...
#Override
public void initialize(URL location, ResourceBundle resources) {
File configFile = new File(System.getProperty("user.dir") + "/Config/config.txt");
if (configFile.exists()) { // Returns true as of now, so the "true" statement of the if statement will be called
config = Util.initConfigFile();
isPlayerFileThere.setValue(config.getPlayerFileLocation().toString());
currentPlayerFileLabel.setText(getIsPlayerFileThere());
} else {
currentPlayerFileLabel.setText(getIsPlayerFileThere());
}
}
...
The FXML line that has been updated:
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
You are overriding the label, so you do no longer use the instance that is defined in your FXML:
#FXML
private Label currentPlayerFileLabel = new Label();
Remove the assignment to a new label.

Categories