Search code examples
javajavafxnetbeansfxml

Java fxml return value from form


I'm having an issue where I can't seem to return values from a new form in javafx using fxml.

I'm trying to have a main form which has 4 functions in relation to the game of connect 4 Start Game, View Players, View Games, View Player Games

When I start the program the main menu pops up, I then want to click on the "Start Game" button. After that opens it will prompt to enter user 1's information. It then pops up a new form about player information.

The problem i'm having is that after clicking the accept button the variables in the class become null I have tried accessing from the main menu class via instance get methods and also returning back to the main class.

After the ShowAndWait line the variables seem to be nulled out. Howe can i retrieve the variable information (in c# fashion) after the winodow

package Homework_5;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Connect4Window extends Application {
    ArrayList<Connect4Game> games = new ArrayList<>();

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("FXML_Files/MainMenu.fxml"));
        primaryStage.setTitle("Connect 4");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

MainMenu class

package Homework_5.FXML_Java_Classes;

import Homework_5.DataClasses.PersonData;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;

public class MainMenu {

    @FXML
    private Button btnViewGames;

    @FXML
    private Button btnViewPlayers;

    @FXML
    private Button btnStartGame;

    @FXML
    private Button btnViewPlayerGames;

    @FXML
    void startGame(ActionEvent event) {
        Alert alert = new Alert(Alert.AlertType.NONE, "Please enter player 1 info");
        alert.setHeaderText(null);
        alert.setTitle("Player 1 info");
        alert.showAndWait();

        PlayerInfo playerInfo = new PlayerInfo();
        playerInfo.Show();
        System.out.println(playerInfo.getTxtName()); //Shows null
        System.out.println(playerInfo.isInfoAccepted()); //Shows null
        System.out.println(playerInfo.getChkComputer()); //Shows null
        System.out.println(playerInfo.getColor()); //Shows null
    }

    @FXML
    void viewPlayers(ActionEvent event) {
        System.out.println("view players");
    }

    @FXML
    void viewPlayerGames(ActionEvent event) {
        System.out.println("view playergames");
    }

    @FXML
    void viewGames(ActionEvent event) {
        System.out.println("view games");
    }
}

Player Info Class

package Homework_5.FXML_Java_Classes;

import Homework_5.DataClasses.PersonData;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class PlayerInfo {

    @FXML
    private Button btnAccept;

    @FXML
    private ColorPicker colorColorPicker;

    @FXML
    private CheckBox chkComputer;

    @FXML
    private TextField txtName;

    @FXML
    private Button btnClear;

    private boolean infoAccepted = false;

    public PlayerInfo(){

    }

    public PersonData Show()
    {
        try{
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("../FXML_Files/PlayerInfo.fxml"));
            Parent root1 = fxmlLoader.load();
            Stage stage = new Stage();
            stage.initModality(Modality.WINDOW_MODAL);
            stage.setTitle("Enter Player Information");
            stage.setScene(new Scene(root1));
            stage.showAndWait();
            return new PersonData(txtName.getText(), colorColorPicker.getValue(), chkComputer.isSelected());
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    @FXML
    void Accept(ActionEvent event) {
        if (txtName.getText().isEmpty()) {
            Alert alert = new Alert(Alert.AlertType.WARNING, "The name field is blank!\n\nPlease enter a name.");
            alert.setHeaderText(null);
            alert.setTitle("Name Blank");
            alert.showAndWait();
        } else {
            infoAccepted = true;
            Stage stage = (Stage) btnAccept.getScene().getWindow();
            stage.close();
        }
    }

    @FXML
    void Clear(ActionEvent event) {
        txtName.clear();
        colorColorPicker.setValue(Color.WHITE);
        chkComputer.setSelected(false);
    }

    public Color getColor() {
        return colorColorPicker.getValue();
    }

    public boolean getChkComputer() {
        return chkComputer.isSelected();
    }

    public String getTxtName() {
        return txtName.getText();
    }

    public boolean isInfoAccepted() {
        return infoAccepted;
    }
}

MainMenu fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane prefHeight="81.0" prefWidth="442.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Homework_5.FXML_Java_Classes.MainMenu">
   <children>
      <Button fx:id="btnStartGame" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" onAction="#startGame" prefHeight="55.0" prefWidth="86.0" text="Start Game" />
      <Button fx:id="btnViewPlayers" layoutX="110.0" layoutY="14.0" mnemonicParsing="false" onAction="#viewPlayers" prefHeight="55.0" prefWidth="86.0" text="View Players" />
      <Button fx:id="btnViewPlayerGames" layoutX="302.0" layoutY="14.0" mnemonicParsing="false" onAction="#viewPlayerGames" prefHeight="55.0" prefWidth="127.0" text="View Player Games" />
      <Button fx:id="btnViewGames" layoutX="206.0" layoutY="14.0" mnemonicParsing="false" onAction="#viewGames" prefHeight="55.0" prefWidth="86.0" text="View Games" />
   </children>
</AnchorPane>

Player Info fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane prefHeight="201.0" prefWidth="213.0" rotate="-0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Homework_5.FXML_Java_Classes.PlayerInfo">
   <children>
      <Button fx:id="btnAccept" cancelButton="true" layoutX="14.0" layoutY="165.0" mnemonicParsing="false" onAction="#Accept" text="Accept" />
      <Button fx:id="btnClear" layoutX="156.0" layoutY="165.0" mnemonicParsing="false" onAction="#Clear" text="Clear" />
      <CheckBox fx:id="chkComputer" layoutX="57.0" layoutY="126.0" mnemonicParsing="false" text="Computer" />
      <ColorPicker fx:id="colorColorPicker" layoutX="57.0" layoutY="81.0" />
      <TextField fx:id="txtName" layoutX="57.0" layoutY="41.0" />
      <Label contentDisplay="CENTER" layoutX="14.0" layoutY="14.0" text="Please Enter User Information below" />
      <Label layoutX="14.0" layoutY="45.0" text="Name:" />
      <Label layoutX="14.0" layoutY="85.0" text="Color:" />
   </children>
</AnchorPane>

Solution

  • The PersonInfo instance you call Show() on is not the one used with PlayerInfo.fxml. FXMLLoader creates a new instance of the controller class, if the fx:controller attribute is provided. To access this instance, use FXMLLoader's getController method after calling load().

    You could e.g. create a static method loading the and use it to create the controller:

    public class PlayerInfo {
    
        @FXML
        private Parent root1;
    
        public static PlayerInfo create() throws Exception {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("../FXML_Files/PlayerInfo.fxml"));
            fxmlLoader.load();
            return fxmlLoader.getController();
        }
    
        public PersonData Show() {
            Stage stage = new Stage();
            stage.initModality(Modality.WINDOW_MODAL);
            stage.setTitle("Enter Player Information");
            stage.setScene(new Scene(root1));
            stage.showAndWait();
            return new PersonData(txtName.getText(), colorColorPicker.getValue(), chkComputer.isSelected());
        }
        ...
    }
    
    <AnchorPane fx:id="root1" prefHeight="201.0" prefWidth="213.0" rotate="-0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Homework_5.FXML_Java_Classes.PlayerInfo">
        ...
    
    PlayerInfo playerInfo = PlayerInfo.create();
    playerInfo.Show();
    

    Alternatively you could use <fx:root> and load the fxml from the constructor of the "controller", see the custom component approach in Introduction to FXML