我一直在尝试创建我的 OptionsMenu 类的实例,但我在场景中的所有 FXML 组件都显示为 null,我不确定为什么。我检查了 FXML 控制器、组件的 fx::id、@FXML 标头,到目前为止,没有任何效果。如果有人能给我一些指导,将不胜感激。
这是我要加载的 OptionsMenu() 类:
package ui_gui;
import controllers.Chess;
import controllers.Main;
import enums.GameColor;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.beans.value.ChangeListener;
import model.GameStateEditor;
import model.Options;
import java.io.IOException;
public class OptionsMenu extends Pane implements EventHandler<ActionEvent> {
/**The root of the layout**/
private Pane root;
/**the Scene on to which the root is situated**/
private Scene scene;
public enum Screen {SCREEN3, SCREEN4};
/**A button on the layout**/
@FXML
Button blackSquareButton;
@FXML
Button whiteSquareButton;
@FXML
CheckBox undosEnabledCheckbox;
@FXML
CheckBox unlimitedUndosCheckbox;
@FXML
CheckBox showMovesCheckbox;
@FXML
Button saveButton;
@FXML
TextField undoTextField;
@FXML
Button exitButton;
ScreenChangeHandler screenChanger;
public static Chess game;
public static OptionsMenu instance;
/**Set the handler for sreen changes**/
public void setScreenChangeHandler(ScreenChangeHandler sch){
this.screenChanger = sch;
}//end
/**
* Create a singleton instance of a ColorChooser
* @return The instance of this ColorChooser
*/
public static OptionsMenu getInstance(Chess game){
if (instance == null){
instance = new OptionsMenu(game);
}
return instance;
}//end getInstance
@FXML
public void initialize() {
blackSquareButton.setOnAction(this::handle);
System.out.println(blackSquareButton.getId());
whiteSquareButton.setOnAction(this::handle);
saveButton.setOnAction(this::handle);
exitButton.setOnAction(this::handle);
}
public OptionsMenu(Chess chessGame) {
super();
FXMLLoader loader = new FXMLLoader(getClass().getResource("options-view.fxml"));
//loader.setController(new Main());
try {
root = loader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
this.game = chessGame;
Options options = game.getOptions();
int[] blkColors = options.getBlackSquareColor();
// Setting the initial color of the blackSquareButton (to black)
Color black = Color.rgb(blkColors[0], blkColors[1], blkColors[2]);
// Recolor OptionMenu's button with the value chosen in our color picker
BackgroundFill blackFill = new BackgroundFill(black, CornerRadii.EMPTY, Insets.EMPTY);
Background backGround = new Background(blackFill);
blackSquareButton.setBackground(backGround);
int[] whtColors = options.getWhiteSquareColor();
// Setting the initial color of the whiteSquareButton (to white)
Color white = Color.rgb(whtColors[0], whtColors[1], whtColors[2]);
// Recolor OptionMenu's button with the value chosen in our color picker
BackgroundFill whiteFill = new BackgroundFill(white, CornerRadii.EMPTY, Insets.EMPTY);
Background whiteGround = new Background(whiteFill);
whiteSquareButton.setBackground(whiteGround);
undosEnabledCheckbox.setSelected(options.getUndosEnabled());
showMovesCheckbox.setSelected(options.getShowMoves());
if(undosEnabledCheckbox.isSelected()){
if(options.getUnlimitedUndos() == true){
unlimitedUndosCheckbox.setSelected(true);
undoTextField.setDisable(true);
}else{// If we don't have unlimited undos in our settings
if(options.getUndoMax() != 999){ // 999 represents having unlimited undos
// Set the "undos" text field to the maximum amount of undos in Options()
undoTextField.setText(String.valueOf(options.getUndoMax()));
}
}
}
// Create a ChangeListener that listens for changes in the selected property of the checkbox
ChangeListener<Boolean> listener1 = new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
undoTextField.setDisable(false); // Enable if the checkbox is selected
unlimitedUndosCheckbox.setDisable(false);
} else {
unlimitedUndosCheckbox.setDisable(true);
unlimitedUndosCheckbox.setSelected(false);
undoTextField.clear();
if(undoTextField.isDisabled() == false){
undoTextField.setDisable(true);
}
}
}
};
// Add the ChangeListener to the checkbox's selected property
undosEnabledCheckbox.selectedProperty().addListener(listener1);
ChangeListener<Boolean> listener2 = new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
undoTextField.setDisable(true); // If checked, disable text field
undoTextField.clear();
} else {
undoTextField.setDisable(false);
}
}
};
unlimitedUndosCheckbox.selectedProperty().addListener(listener2);
}
/**Get the root of the layout in order for another class to display it**/
public Pane getRoot() {
return root;
}
/**
* Handles an action event generated by a component in our scene
* @param actionEvent the event which occurred
*/
@Override
public void handle(ActionEvent actionEvent) {
if (actionEvent.getSource() == blackSquareButton) {
openColorChooser(blackSquareButton);
}else if(actionEvent.getSource() == whiteSquareButton) {
openColorChooser(whiteSquareButton);
}else if(actionEvent.getSource() == saveButton){
saveSettings();
}else if(actionEvent.getSource() == exitButton){
//exitSettings();
}
}
/**
* Saves the Options Menu Screen's settings to the Chess games Options()
*/
public void saveSettings(){
// Getting the game's options
Options options = game.getOptions();
GameStateEditor stateEditor = game.getStateEditor();
// That means we can undo in this game. If the player doesn't select
// anything else, we'll assume they'll undo once at a time
if(undosEnabledCheckbox.isSelected()){
options.setUndosEnabled(true);
// If the player wants unlimited undos
if(unlimitedUndosCheckbox.isSelected()){
options.setUnlimitedUndos(true);
// If the "Max Undos" text field has numbers in it
}else if(0 < undoTextField.getText().length() && undoTextField.getText().length() < 3){
int allowed_undos = Integer.parseInt(undoTextField.getText().trim());
options.setUndoMax(allowed_undos);
stateEditor.setMaxUndos(options.getUndoMax());
}
}else{// If the "Undos Enabled" is not selected
options.setUndosEnabled(false);
}
if(showMovesCheckbox.isSelected()){
options.setShowMoves(true);
}else{ // If the show moves checkbox isn't checked
options.setShowMoves(true);
}
int[] blkColors = getButtonColorInt(blackSquareButton);
int[] whtColors = getButtonColorInt(whiteSquareButton);
// Setting the options black and white square colors
options.setBlackSquareColor(blkColors[0], blkColors[1], blkColors[2]);
options.setWhiteSquareColor(whtColors[0], whtColors[1], whtColors[2]);
}
/**
* Opens a ColorChooser triggered by a button click
* @param button - The button that opened ColorChooser
*/
public void openColorChooser(Button button){
screenChanger.switchScreen(ScreenFactory.Screen.SCREEN4);
/**
ColorChooser ch = null;
ch = ColorChooser.getInstance();
ch.setColor(getButtonColor(button));
//TODO: For task 3 a another method here would be good.
root = ch;
//Change the screen
if(screenChanger == null)
scene = new Scene(root,400,600);
else
scene.setRoot(root);
ch.setPreviousScene(((Node)button).getScene());
ch.setPreviousButton(button);
Stage stage = (Stage) ((Node)button).getScene().getWindow();
stage.setScene(scene);
stage.show();
//runOnce = false;
*/
}
/**
* Returns the Color of a button in the form of a list of integers representing
* RBG values.
* @param button - The button whose color we're getting
* @return A button's color as a list of integers
*/
public int[] getButtonColorInt(Button button){
Color color = (Color) button.getBackground().getFills().get(0).getFill();
int[] buttonColors = {(int)(color.getRed()), (int)(color.getGreen()), (int)(color.getBlue())};
return buttonColors;
}
/**
* Returns the Color of a button in the form of a Hexadecimal string.
* @param button - The button whose color we're getting
* @return A button's color as a Hexadecimal string
*/
public String getButtonColor(Button button){
Color color = (Color) button.getBackground().getFills().get(0).getFill();
String hex = String.format("%02X%02X%02X", (int)(color.getRed()*255),
(int)(color.getGreen()*255), (int)(color.getBlue()*255));
return hex;
}
}
这是我的 FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane minWidth="400.0" prefHeight="400.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.Main">
<children>
<Label alignment="TOP_LEFT" layoutX="150.0" layoutY="20.0" prefHeight="40.0" prefWidth="95.0" text="Settings">
<font>
<Font size="27.0" />
</font>
</Label>
<Label layoutX="39.0" layoutY="76.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="40.0" prefWidth="80.0" text="Colors:">
<font>
<Font size="20.0" />
</font>
</Label>
<HBox alignment="CENTER_LEFT" layoutX="40.0" layoutY="159.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="29.0" prefWidth="157.0">
<children>
<Label contentDisplay="BOTTOM" prefHeight="22.0" prefWidth="110.0" text="Black Squares">
<font>
<Font size="17.0" />
</font>
</Label>
<Button fx:id="blackSquareButton" mnemonicParsing="false" prefHeight="28.0" prefWidth="29.0" text=" " textAlignment="CENTER">
<font>
<Font size="2.0" />
</font>
<HBox.margin>
<Insets left="7.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<VBox layoutX="40.0" layoutY="207.0">
<children>
<Label prefHeight="26.0" prefWidth="89.0" text="Undo:">
<font>
<Font size="20.0" />
</font>
</Label>
</children>
</VBox>
<VBox layoutX="226.0" layoutY="131.0">
<children>
<CheckBox fx:id="showMovesCheckbox" mnemonicParsing="false" prefHeight="33.0" prefWidth="134.0" text="Show moves">
<font>
<Font size="17.0" />
</font>
</CheckBox>
</children>
</VBox>
<VBox alignment="CENTER" layoutX="237.0" layoutY="298.0" prefHeight="48.0" prefWidth="123.0" spacing="8.0">
<children>
<Button fx:id="saveButton" mnemonicParsing="false" text="Save">
<font>
<Font size="17.0" />
</font>
</Button>
<Button fx:id="exitButton" mnemonicParsing="false" text="Exit">
<font>
<Font size="17.0" />
</font>
</Button>
</children>
</VBox>
<VBox alignment="CENTER_LEFT" layoutX="40.0" layoutY="245.0" prefHeight="56.0" prefWidth="157.0" spacing="5.0">
<children>
<CheckBox fx:id="undosEnabledCheckbox" mnemonicParsing="false" text="Enabled">
<font>
<Font size="17.0" />
</font>
<VBox.margin>
<Insets bottom="4.0" />
</VBox.margin>
</CheckBox>
<CheckBox fx:id="unlimitedUndosCheckbox" mnemonicParsing="false" prefHeight="24.0" prefWidth="165.0" text="Unlimited Undos">
<font>
<Font size="17.0" />
</font>
</CheckBox>
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
</VBox>
<HBox layoutX="42.0" layoutY="335.0" prefHeight="22.0" prefWidth="134.0">
<children>
<Label prefHeight="22.0" prefWidth="110.0" text="Max Undos">
<font>
<Font size="17.0" />
</font>
</Label>
<TextField fx:id="undoTextField" prefHeight="22.0" prefWidth="66.0">
<font>
<Font size="17.0" />
</font>
<padding>
<Insets left="8.0" />
</padding>
<HBox.margin>
<Insets left="5.0" />
</HBox.margin>
</TextField>
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutX="39.0" layoutY="116.0" prefHeight="29.0" prefWidth="151.0">
<children>
<Label prefHeight="23.0" prefWidth="108.0" text="White Squares">
<font>
<Font size="17.0" />
</font>
</Label>
<Button fx:id="whiteSquareButton" mnemonicParsing="false" prefHeight="26.0" prefWidth="29.0" text=" ">
<font>
<Font size="12.0" />
</font>
<HBox.margin>
<Insets left="9.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</children>
</AnchorPane>
这是我的控制器类:
package controllers;
import interfaces.guiIF;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import ui_gui.MainMenu;
import ui_gui.ScreenFactory;
/**
* A class to start the chess application
*/
public class Main extends Application {
/**
* Creates a driver and calls runGame.
*/
private static Chess game;
public static void main(String[] args) {
// guiIF GUI = MainMenu.getMenu();
game = Chess.getInstanceOfGame();
Driver driver = new Driver(game);
driver.runGame();
launch(args);
}
/**
*
* @param primaryStage the primary stage for this application, onto which
* the application scene can be set.
* Applications may create other stages, if needed, but they will not be
* primary stages.
*/
@Override
public void start(Stage primaryStage){
try{
MainMenu mainScreen = new MainMenu(game);
Scene scene = new Scene(mainScreen.getRoot(), 1000, 600);
ScreenFactory.getInstance(scene);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
catch(Exception e){
e.printStackTrace();
}
}
}
这是我在尝试加载场景时遇到的错误:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "javafx.scene.control.Button.setBackground(javafx.scene.layout.Background)" because "this.blackSquareButton" is null
at ui_gui.OptionsMenu.<init>(OptionsMenu.java:114)
at ui_gui.OptionsMenu.getInstance(OptionsMenu.java:79)
at ui_gui.ScreenFactory.setScreen(ScreenFactory.java:106)
at ui_gui.ScreenFactory.switchScreen(ScreenFactory.java:165)
at ui_gui.MainMenu$1.handle(MainMenu.java:245)
at ui_gui.MainMenu$1.handle(MainMenu.java:213)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8944)
at javafx.controls/javafx.scene.control.Button.fire(Button.java:203)
at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:207)
at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3980)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1890)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2704)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:937)
at javafx.graphics/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)