为什么在尝试加载 JavaFX 类时我的所有 FXML 组件都是空的?

问题描述 投票:0回答:0

我一直在尝试创建我的 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)
java javafx javafx-8
© www.soinside.com 2019 - 2024. All rights reserved.