Question

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

Was it helpful?

Solution

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.)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top