卡在不会将可注入字段加载到 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 不可变会稍微降低更新的复杂性,因为您不再需要使用绑定,而只需要进行赋值。此外,只有一个船舶+人员场景将允许您删除解除绑定逻辑,进一步简化逻辑...
我遇到的唯一问题是 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 不可变会稍微降低更新的复杂性,因为您不再需要使用绑定,而只需要进行赋值。此外,只有一个船舶+人员场景将允许您删除解除绑定逻辑,进一步简化逻辑...