To start with, I have this main FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import binpacking.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.collections.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<AnchorPane id="AnchorPane" prefHeight="582.0" prefWidth="556.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="binpacking.FXMLDocumentController">
<children>
<Label id="TypeLabel" fx:id="typeLabel" layoutX="186.0" layoutY="77.0" text="Type:" />
<ComboBox id="TypeOfAlgorithm" fx:id="typeOfAlgorithmCombo" layoutX="239.0" layoutY="77.0">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="First-Fit" />
<String fx:value="First-Fit Decreasing" />
<String fx:value="Full-Bin Packing" />
</FXCollections>
</items>
</ComboBox>
<Label fx:id="numbersLabel" layoutX="242.0" layoutY="148.0" prefWidth="65.0" text="Numbers:" />
<Group id="Group" layoutX="138.0" layoutY="173.0" scaleX="1.0" scaleY="1.0">
<children>
<TextField id="no1" fx:id="no1Field" layoutX="0.0" layoutY="0.0" prefHeight="21.0" prefWidth="34.999900000002526" />
<TextField id="no2" fx:id="no2Field" layoutX="43.0" layoutY="0.0" prefHeight="21.0" prefWidth="34.999900000002526" />
<TextField id="no3Field" fx:id="no3Field" layoutX="95.0" layoutY="0.0" prefHeight="21.0" prefWidth="34.9998779296875" />
<TextField id="no4Field" fx:id="no4Field" layoutX="148.0" layoutY="0.0" prefHeight="21.0" prefWidth="34.9998779296875" />
<TextField id="no5Field" fx:id="no5Field" layoutX="191.0" layoutY="0.0" prefHeight="21.0" prefWidth="34.9998779296875" />
<TextField id="no6Field" fx:id="no6Field" layoutX="236.0" layoutY="0.0" prefHeight="21.0" prefWidth="34.9998779296875" />
</children>
</Group>
<Label id="noOfBinsLabel" fx:id="noOfBinsLabel" layoutX="224.0" layoutY="291.0" text="Number of Bins:" />
<Label id="ofSizeLabel" fx:id="ofSizeLabel" layoutX="228.0" layoutY="360.0" text="of Size:" />
<TextField id="ofSizeField" fx:id="ofSizeField" layoutX="286.0" layoutY="360.0" prefHeight="21.0" prefWidth="34.9998779296875" />
<Button fx:id="buildButton" layoutX="171.0" layoutY="507.0" mnemonicParsing="false" onAction="#onBuildClicked" text="Build Window" />
<Button fx:id="resetButton" layoutX="300.0" layoutY="508.0" mnemonicParsing="false" onAction="#onResetClicked" text="Reset" />
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="136.0" layoutY="15.0" text="The Bin Packing Program" textOverrun="CENTER_ELLIPSIS">
<font>
<Font name="System Bold" size="22.0" />
</font>
</Label>
<MenuBar fx:id="MenuBar" layoutX="0.0" layoutY="0.0" useSystemMenuBar="true">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</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>
<Slider fx:id="noOfBinsSlider" layoutX="203.0" layoutY="325.0" max="6.0" min="1.0" value="6.0" />
</children>
</AnchorPane>
And I have the controller class for it:
package binpacking;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class FXMLDocumentController implements Initializable {
// Declaration of strings which hold user data input
private String typeOfAlgorithmString;
private String numberOfBins;
private String binSize;
private String[] numbers = new String[6];
@FXML
private Label typeLabel;
@FXML
private ComboBox<?> typeOfAlgorithmCombo;
@FXML
private TextField no1Field;
@FXML
private TextField no2Field;
@FXML
private TextField no3Field;
@FXML
private TextField no4Field;
@FXML
private TextField no5Field;
@FXML
private TextField no6Field;
@FXML
private Label noOfBinsLabel;
@FXML
private Label ofSizeLabel;
@FXML
private TextField ofSizeField;
@FXML
private Label numbersLabel;
@FXML
private Button buildButton;
@FXML
private Button resetButton;
@FXML
private MenuBar menuBar;
@FXML
private Slider noOfBinsSlider;
@Override
public void initialize(URL url, ResourceBundle rb) {
// Menu bar preparation code to be added here
}
public void changeStage(String fxmlName) throws Exception {
// Load FXML - based on the fxmlName parameter
Parent root = FXMLLoader.load(getClass().getResource(fxmlName + ".fxml"));
// Create objects of the stage and scene
Stage stage = new Stage();
Scene scene = new Scene(root);
// Show the new stage/open new window
stage.setScene(scene);
// Detect title for the window
if (fxmlName == "FirstFit")
{
stage.setTitle("First-Fit");
} else if (fxmlName == "FirstFitDecreasing") {
stage.setTitle("First-Fit Decreasing");
} else {
stage.setTitle("Full-Bin Packing");
}
stage.show();
}
@FXML
private void onBuildClicked(ActionEvent event) {
// Get Type Of Algorithm
typeOfAlgorithmString = (String) typeOfAlgorithmCombo.getValue();
// Get Numbers Input
numbers[0] = no1Field.getText();
numbers[1] = no2Field.getText();
numbers[2] = no3Field.getText();
numbers[3] = no4Field.getText();
numbers[4] = no5Field.getText();
numbers[5] = no6Field.getText();
// Get Number Of Bins
// numberOfBins = String.valueOf(noOfBinsSlider.getValue());
// Get Bin Size
binSize = ofSizeField.getText();
// Create Objects of Each Algorithm's Class
FirstFit firstFitClass = new FirstFit();
FirstFitDecreasing firstFitDecreasingClass = new FirstFitDecreasing();
FullBin fullBinClass = new FullBin();
// Determine Type Of Algorithm To Run
switch (typeOfAlgorithmString.toLowerCase()) {
case "first-fit":
firstFitClass.runAlgorithm();
break;
case "first-fit decreasing":
firstFitDecreasingClass.runAlgorithm();
break;
case "full-bin packing":
fullBinClass.runAlgorithm();
break;
default:
System.out.println("Invalid algorithm type.");
break;
}
}
@FXML
private void onResetClicked(ActionEvent event) {
no1Field.setText(null);
no2Field.setText(null);
no3Field.setText(null);
no4Field.setText(null);
no5Field.setText(null);
no6Field.setText(null);
ofSizeField.setText(null);
}
public String getBinSize() {
if (binSize == null) {
binSize = "Bin size field is null.";
}
return binSize;
}
}
And at last, I have another controller class FirstFitController, which has the initialise method set like this:
@Override
public void initialize(URL url, ResourceBundle rb) {
// Create object of main controller so we can access the fields
FXMLDocumentController mainController = new FXMLDocumentController();
// Get and print value of bin size label
System.out.println(mainController.getBinSize());
// binSizeBottomLabel.setText("Bin size: " + mainController.getBinSize());
}
Basically, the user selects First-Fit from the drop down menu in the first window (which I posted the FXML file for at the very top) and also enters values in every field (specifically for this problem please note "ofSizeField"). Then another window opens and it is supposed to print to the console the value of what the user has entered in the "ofSizeField" (using the getBinSize method) - but every time it returns as null even though I enter something in it.
I understand I would get the NullPointerException error if I didn't have the if statement in the getBinSize method to check whether or not it is equal to null, but I don't understand what I have done wrong because after the "Build Window" button is clicked and the program executes the "onBuildClicked" method, the variable "binSize" is set like this: binSize = ofSizeField.getText();
and then the method getBinSize is called which returns null.
If anyone could please help me with this and tell me what I am doing wrong, I would appreciate it a lot. Thanks in advance
EDIT -
So my changeStage(String fxmlName) contains these lines of code:
public void changeStage(String fxmlName) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource(fxmlName + ".fxml"));
FirstFitController ffc = (FirstFitController) fxmlLoader.getController();
// Create objects of the stage and scene
Stage stage = new Stage();
Scene scene = new Scene(p);
// Show the new stage/open new window
stage.setScene(scene);
// Detect title for the window
if (fxmlName == "FirstFit")
{
stage.setTitle("First-Fit");
} else if (fxmlName == "FirstFitDecreasing") {
stage.setTitle("First-Fit Decreasing");
} else {
stage.setTitle("Full-Bin Packing");
}
stage.show();
}
But firstly, it seems that the load method of FXMLLoader is a static method - so why is it necessary to create an object? And secondly, I didn't quite understand how I would use "ffc" (the instance of FirstFitController)?
Thanks
A little guess work from my side:
You start with an instance of the FXMLDocumentController
where the user enters the values.
Then the FirstFitController
gets called as the other window (is that correct?). It creates an instance of the FXMLDocumentController
in its initialize
method. Now that instance is a different one, than the first one in which the user entered the values. Hence binSize
is null
.
The solution is to pass a reference for the first FXMLDocumentController
to the FirstFitController
from which it can read the values (especially don't create a new instance). (Alternatively create a separate class which holds the actual values and pass that around.)
EDIT:
When you load the FXML for the FirstFitController, do this:
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource(fxmlName + ".fxml"));
FirstFitController ffc = (FirstFitController) fxmlLoader.getController();
Then you can access the actual instance of the FirstFitController
and set any members as you wish. (You need to create an instance of FxmlLoader
to retrieve the controller.)