卡在不会将可注入字段加载到 FXML 文档中的 javafx 程序

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

我遇到的唯一问题是 getAll() 方法,它似乎立即运行在我在 PersonnelController class 中的位置。我也在很多其他地方尝试过,但无济于事。 setAll() 方法看起来不错,我已经测试过 setAll() 传递值的方法可以 return 将正确的值传递给 main 方法。他们只是出于某种原因没有在我需要的正确时间 return 向控制器发送值。

应该发生的是用户输入他们的用户名和密码,然后单击登录按钮,这就是在加载 secondScene 之前将可注入值传递给 FXML 的地方。根据他们输入的用户名,第二个场景中会出现一组不同的图片和标签。我有程序调用的 getter 和 setter 方法来设置文本和图像。

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

而这里的 Starship.java 是两个 class 之一,其中包含 getter 和 setter 方法,由控制器中的 setAll() 方法调用第二个屏幕的登录屏幕和控制器中的 getAll() 方法 (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>

登录后,设置了 none 应由 getter 方法 return 编辑的标签或图像。我只得到我手动输入的 "Welcome" 字符串。所以,标题说 "Welcome, null."

initialize方法在load的调用过程中被FXMLLoader调用。那个时候none的属性已经设置好了,以后就"preload"现场了。

我强烈建议不要使用 static 数据来传递信息。当然,能够访问数据很方便,但这是以无法控制 when/by 修改值的代码并且永远不能同时拥有多艘船为代价的。

您可以改为使用 javafx 属性来处理此问题。以下 class 设计只是缩写,缺少适当的可见性修饰符、setter、(属性) getter、初始化和构造函数,但我相信您可以自己添加:

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

这允许您收听船舶数据的修改:

Starship ship = new Starship();

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

登录控制器

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

人事控制器

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

这应该允许您在 JavaFX 应用程序线程的任何位置更新 Starship 对象,并查看人员场景中的变化。使 CrewMember class 不可变会稍微降低更新的复杂性,因为您不再需要使用绑定,而只需要进行赋值。此外,只有一个船舶+人员场景将允许您删除解除绑定逻辑,进一步简化逻辑...