简体   繁体   中英

Stuck on a javafx program that won't load the injectable fields into FXML document

The only issue I'm having with this is the getAll() method, it seems that it runs immediately where I have it in the PersonnelController class. I've tried it in many other spots as well though, to no avail. The setAll() method seems fine and I've tested that the methods which setAll() is passing values to can return the correct values to the main method. They just for some reason don't return the values to the controller at the correct time I need them to.

What is supposed to happen is that a user enters in their username and password, then clicks the login button and that is where the injectable values can be passed to the FXML, before the secondScene loads. Depending on the username that they entered, a different set of pictures and labels will appear on the second scene. I have getter and setter method that the program is calling to set the text and images.

PersonnelController.java

package application.controller;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import application.Main;
import application.model.CrewMember;
import application.model.Starship;
import application.model.User;

public class PersonnelController implements Initializable {

    @FXML Label welcomeMsg;

    @FXML Label shipName;

    @FXML
    ImageView commandOfficer;

    @FXML
    ImageView engineOfficer;

    @FXML
    ImageView commOfficer;

    @FXML
    ImageView firstOfficer;

    @FXML
    ImageView nurse;

    @FXML
    ImageView medOfficer;

    @FXML
    ImageView navigator;

    @FXML
    ImageView helmsman;

    @FXML
    Label crew8;

    @FXML
    Label crew7;

    @FXML
    Label crew6;

    @FXML
    Label crew5;

    @FXML
    Label crew4;

    @FXML
    Label crew3;

    @FXML
    Label crew2;

    @FXML
    Label crew1;


    private Scene firstScene;

    public void getAll() {
          welcomeMsg.setText("Welcome, " + Starship.getCapt());
          shipName.setText(Starship.getShip());

          commandOfficer.setImage(CrewMember.getInput1());
          firstOfficer.setImage(CrewMember.getInput2());
          commOfficer.setImage(CrewMember.getInput3());
          engineOfficer.setImage(CrewMember.getInput4());
          helmsman.setImage(CrewMember.getInput5());
          navigator.setImage(CrewMember.getInput6());
          medOfficer.setImage(CrewMember.getInput7());
          nurse.setImage(CrewMember.getInput8());

          crew1.setText(CrewMember.getCrew1());
          crew2.setText(CrewMember.getCrew2());
          crew3.setText(CrewMember.getCrew3());
          crew4.setText(CrewMember.getCrew4());
          crew5.setText(CrewMember.getCrew5());
          crew6.setText(CrewMember.getCrew6());
          crew7.setText(CrewMember.getCrew7());
          crew8.setText(CrewMember.getCrew8());
}

    public void setFirstScene(Scene scene) {
        firstScene = scene;
    }


    public void openFirstScene(ActionEvent actionEvent) {    
        Stage primaryStage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        primaryStage.setScene(firstScene);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
                getAll();
            }
}

LoginController.java

package application.controller;

import java.io.FileNotFoundException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.Scanner;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;

import application.Main;
import application.model.CrewMember;
import application.model.Starship;
import application.model.User;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;

import java.awt.Label;
import java.io.*;

public class LoginController implements Initializable {

    @FXML 
    private Button loginButton;

    @FXML
    private TextField username;

    @FXML
    private PasswordField password;

    private static Scene secondScene;   

    private void setAll(String user) {

        Starship.setShip(user);
        Starship.setCapt(user);

        CrewMember.setInput1(user);
        CrewMember.setInput2(user);
        CrewMember.setInput3(user);
        CrewMember.setInput4(user);
        CrewMember.setInput5(user);
        CrewMember.setInput6(user);
        CrewMember.setInput7(user);
        CrewMember.setInput8(user);

        CrewMember.setCrew1(user);
        CrewMember.setCrew2(user);
        CrewMember.setCrew3(user);
        CrewMember.setCrew4(user);
        CrewMember.setCrew5(user);
        CrewMember.setCrew6(user);
        CrewMember.setCrew7(user);
        CrewMember.setCrew8(user);


}

    public void setSecondScene(Scene scene) {
        secondScene = scene;
    }

    public void openSecondScene(ActionEvent actionEvent) {

        String user = username.getText();
        String pass = password.getText();
        setAll(user);   



         /* If the username/password are valid, open the Personnel viewer. 
        Otherwise, alert the user that credentials are invalid. */
        if(User.validate(user,pass,"data/users.csv")== true) {  

            Stage primaryStage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();  
            primaryStage.setScene(secondScene);


          }
        else{

            User.invalid();

                }   
    }


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        assert loginButton != null : "fx:id=\"loginButton\" was not injected: check your FXML file '/Login.fxml'.";

            }
}

Main.java

package application;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

import application.model.CrewMember;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {

        try {
            /* getting loader and a pane for the first scene.
            Loader will then give a possibility to get related controller */
            FXMLLoader firstPaneLoader = new FXMLLoader(getClass().getResource("/Login.fxml"));
            Parent firstPane = firstPaneLoader.load();
            Scene firstScene = new Scene(firstPane);

            // getting loader and a pane for the second scene
            FXMLLoader secondPageLoader = new FXMLLoader(getClass().getResource("/Personnel.fxml"));
            Parent secondPane = secondPageLoader.load();
            Scene secondScene = new Scene(secondPane);

            // injecting second scene into the controller of the first scene
            application.controller.LoginController firstPaneController = (application.controller.LoginController) firstPaneLoader.getController();
            firstPaneController.setSecondScene(secondScene);

            // injecting first scene into the controller of the second scene
            application.controller.PersonnelController secondPaneController = (application.controller.PersonnelController) secondPageLoader.getController();
            secondPaneController.setFirstScene(firstScene);

            // open the login screen on start       
            primaryStage.setScene(firstScene);
            primaryStage.show();

        } catch(Exception e) {
            e.printStackTrace();
        }

            }

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

And here Starship.java is one of two classes with getter and setter methods that are called on by the setAll() method in the controller for the login screen and getAll() method in the controller for the second screen (Personnel.FXML).

Personnel.fxml

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>

<AnchorPane prefHeight="480.0" prefWidth="720.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.PersonnelController">
   <children>
      <ImageView fitHeight="90.0" fitWidth="120.0" onMouseClicked="#getAll" pickOnBounds="true" preserveRatio="true">
         <image>
            <Image url="@../Starfleet_Command_logo.jpg" />
         </image>
      </ImageView>
      <Label fx:id="welcomeMsg" layoutX="302.0" layoutY="45.0" prefHeight="16.0" prefWidth="116.0" AnchorPane.bottomAnchor="419.0" AnchorPane.leftAnchor="302.0" AnchorPane.rightAnchor="302.0" AnchorPane.topAnchor="45.0" />
      <Label fx:id="shipName" layoutX="302.0" layoutY="74.0" prefHeight="14.0" prefWidth="91.0" AnchorPane.bottomAnchor="390.0" AnchorPane.leftAnchor="302.0" AnchorPane.rightAnchor="302.0" AnchorPane.topAnchor="74.0">
         <font>
            <Font size="10.0" />
         </font>
      </Label>
      <GridPane layoutX="8.0" layoutY="131.0" prefHeight="347.0" prefWidth="703.0">
        <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
            <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <ImageView fx:id="commandOfficer" fitHeight="120.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" GridPane.halignment="CENTER" GridPane.valignment="TOP" />
            <ImageView fx:id="engineOfficer" fitHeight="120.0" fitWidth="100.0" layoutX="48.0" layoutY="10.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.valignment="TOP" />
            <ImageView fx:id="commOfficer" fitHeight="120.0" fitWidth="100.0" layoutX="10.0" layoutY="37.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="TOP" />
            <ImageView fx:id="firstOfficer" fitHeight="120.0" fitWidth="100.0" layoutX="10.0" layoutY="37.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="TOP" />
            <ImageView fx:id="nurse" fitHeight="120.0" fitWidth="100.0" layoutX="48.0" layoutY="10.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP" />
            <ImageView fx:id="medOfficer" fitHeight="120.0" fitWidth="100.0" layoutX="10.0" layoutY="37.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP" />
            <ImageView fx:id="navigator" fitHeight="120.0" fitWidth="100.0" layoutX="10.0" layoutY="37.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP" />
            <ImageView fx:id="helmsman" fitHeight="120.0" fitWidth="100.0" layoutX="10.0" layoutY="37.0" pickOnBounds="true" preserveRatio="true" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="TOP" />
            <Label text="Commanding Officer" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label layoutX="80.0" layoutY="138.0" text="Chief Medical Officer" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label layoutX="10.0" layoutY="89.0" text="Navigator" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label layoutX="10.0" layoutY="89.0" text="Helmsman" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label layoutX="10.0" layoutY="89.0" text="Chief Engineering Officer" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <padding>
                  <Insets bottom="30.0" />
               </padding>
            </Label>
            <Label layoutX="10.0" layoutY="89.0" text="Communications Officer" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label layoutX="10.0" layoutY="89.0" text="First Officer" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label layoutX="80.0" layoutY="138.0" text="Nurse" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="30.0" />
               </GridPane.margin>
            </Label>
            <Label fx:id="crew1" text="Label" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew8" layoutX="10.0" layoutY="89.0" text="Label" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew7" layoutX="10.0" layoutY="89.0" text="Label" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew6" layoutX="10.0" layoutY="89.0" text="Label" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew5" layoutX="10.0" layoutY="89.0" text="Label" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew4" layoutX="10.0" layoutY="89.0" text="Label" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew3" layoutX="10.0" layoutY="89.0" text="Label" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
            <Label fx:id="crew2" layoutX="10.0" layoutY="89.0" text="Label" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="BOTTOM">
               <GridPane.margin>
                  <Insets bottom="10.0" />
               </GridPane.margin></Label>
         </children>
      </GridPane>
      <Button layoutX="627.0" layoutY="32.0" mnemonicParsing="false" onAction="#openFirstScene" text="Log Out" />
   </children>
</AnchorPane>

After logging in, none of the labels or images that should be returned by the getter methods are set. I get only the "Welcome" string that I entered manually. So, the title says "Welcome, null."

The initialize method is called by FXMLLoader during the call of load . At that time none of the properties have been set, since you "preload" the scene.

I strongly recommend not using static data to pass around info. Sure it's convenient to be able to access the data, but this comes at the expense of not having any control on when/by what code the values are modified and never being able to have more than a single ship at once.

You could instead use javafx properties to deal with this. The following class design is just abreviated missing the proper visibility modifiers, setters, (property) getters, initialisations and constructors, but I'm sure you could add this on your own:

class Starship

   StringProperty name;
   ObjectProperty<CrewMember> captain;

   // possibly replace the following property with individual properties for roles
   // or use a ObservableMap<Role, CrewMember> instead???
   ObservableList<CrewMember> crew;
}

// could we make this immutable instead??? 
class CrewMember {
    StringProperty name;
    ObjectProperty<Image> image;
}

This allows you to listen to modifications of the ship data:

Starship ship = new Starship();

firstPaneController.setShip(ship);
secondPaneController.setShip(ship);

LoginController

private Starship ship;

public void setShip(Starship ship) {
    this.ship = ship;
}

private void setAll(String user) {
    ship.setCaptain(new CrewMember(user));

    ship.getCrew().setAll(Stream.generate(() -> new CrewMember(user))
                                .limit(8)
                                .toArray(CrewMember[]::new));
}

PersonnelController

private Label[] crewNameLabels;
private ImageView[] crewImageViews;

@Override
public void initialize(URL location, ResourceBundle resources) {
    crewNameLabels = new Label[] { crew1, crew2, crew3, crew4, crew5, crew6, crew7, crew8  };
    crewImageViews = new ImageView[] {
        commandOfficer,
        firstOfficer,
        commOfficer,
        engineOfficer,
        helmsman,
        navigator,
        medOfficer,
        nurse
    };
}

private final InvalidationListener crewUpdater = o -> {
    int newCrewSize = 0;
    if (ship != null) {
        List<Crew> crew = ship.getCrew();
        newCrewSize = ship.getCrew().size();
        if (newCrewSize > crewNameLabels.length) {
            newCrewSize = crewNameLabels.length;
        }
        for (int i = 0; i < newCrewSize; i++) {
            CrewMember c = crew.get(i);
            crewNameLabels[i].textProperty().bind(c.nameProperty());
            crewImageViews[i].imageProperty().bind(c.imageProperty());
        }
    }

    // unbind everything that has no corresponding CrewMember
    for (int i = newCrewSize; i < crewNameLabels.length; i++) {
        crewNameLabels[i].textProperty().unbind();
        crewNameLabels[i].setText("");
        crewImageViews[i].imageProperty().unbind();
        crewImageViews[i].setImage(null);
    }
};

private final InvalidationListener captainUpdater = o -> {
    CrewMember captain = null;
    if (ship != null) {
        captain = ship.getCaptain();
    }
    if (captain == null) {
        welcomeMsg.textProperty().unbind();
        welcomeMsg.setText("");
    } else {
        welcomeMessage.bind(Bindings.concat("Welcome, ", captain.nameProperty()));
    }
};

private Starship ship;

public void setShip(Starship ship) {
    if (this.ship != null) {
        // remove old listener when replacing the ship
        this.ship.getCrew().removeListener(crewUpdater);
        this.ship.captainProperty().removeListener(captainUpdater);
    }

    this.ship = ship;
    if (ship != null) {
        ship.getCrew().addListener(crewUpdater);
        ship.captainProperty().addListener(captainUpdater);
        shipName.textProperty().bind(ship.nameProperty());
    } else {
        shipName.textProperty().unbind();
        shipName.setText("");
    }

    // update content
    crewUpdater.invalidated(null);
    captainUpdater.invalidated(null);
}

This should allow you to update the Starship object anywhere on the JavaFX application thread and see the change in the personnel scene. Making the CrewMember class immutable would reduce the complexity of the update a bit, since you'd no longer need to use bindings, but simply would need to do assignments. Also only ever having a single ship+personnel scene would allow you to remove the unbinding logic further simplifying the logic...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM