【问题标题】:All my @FXML references are null我所有的@FXML 引用都是空的
【发布时间】:2018-05-20 04:47:07
【问题描述】:

我正在尝试在 FlowPane 中显示扑克牌。我有一个主布局和一个嵌套布局。出于某种原因,当我调试 IntelliJ 报告时,两个控制器上的所有字段都用 @FXML 注释为空。

这是我目前所掌握的内容的简化版本。 Full Code on GitHub:

MainWindow.fxml

<BorderPane fx:controller="controller.MainWindowController">
    <center>
        <fx:include fx:id="tableScene" source="TableScene.fxml"/>
    </center>
</BorderPane>

MainWindowController.java

public class MainWindowController implements Initializable {
    @FXML
    MenuBar menuBar;

    @FXML
    Menu fileMenu;

    [...] more fields

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // nothing here in my code
    }

}

TableScene.fxml

<AnchorPane fx:controller="controller.TableSceneController">
    <children>
        <FlowPane fx:id="dealerHandFlowPane"></FlowPane>
        <FlowPane fx:id="playerHandFlowPane"></FlowPane>
    </children>
</AnchorPane>

TableSceneController

public class TableSceneController implements Initializable {

    @FXML
    private FlowPane dealerHandFlowPane;

    @FXML
    private FlowPane playerHandFlowPane;

    public void displayInitialHand(Player player) {
        var cards = new ArrayList<>(player.getHand().getCards());
        for (BlackjackCard card : cards) {
            if(player.getName().equals("Dealer")) {
                dealerHandFlowPane.getChildren().add(new ImageView(getCardFace(card)));
            } else {
                playerHandFlowPane.getChildren().add(new ImageView(getCardFace(card)));
            }
        }
    }

    public void displayHand(Player player) {
      var cards = new ArrayList<>(player.getHand().getCards());
    }

    public Image getCardFace(BlackjackCard card) {
        return new Image("/images/cards/" + card.getRank().getLetter()
            + card.getSuit().getLetter() + ".png");
    }

    public Image getCardBack() {
        String color[] = {"blue","red"};
        String design = "123";
        return new Image("/images/backs/" + color[0] + design.charAt(2));
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // nothing here in my code either
    }

}

BlackjackMain

public class BlackjackMain extends Application {

    private final String MAIN_WINDOW_PATH = "/fxml/MainWindow.fxml";
    private final String ICON_PATH = "/images/blackjack_icon.png";
    private final String MAIN_STYLE_PATH = "/css/MainWindow.css";
    private final String TABLE_STYLE_PATH = "/css/TableScene.css";
    private final Image MAIN_ICON = new Image(getClass().getResourceAsStream(ICON_PATH));

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Blackjack");
        // close the app gracefully when the 'X' is clicked
        primaryStage.setOnCloseRequest(e -> Platform.exit());
        primaryStage.centerOnScreen();
        primaryStage.setResizable(false);
        initializeMainWindow(primaryStage);
        primaryStage.getIcons().add(MAIN_ICON);
        primaryStage.show();
        primaryStage.toFront();
        initializeGame();
    }

    public void initializeMainWindow(Stage primaryStage) {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource(MAIN_WINDOW_PATH));
        try {
          Parent mainWindow = loader.load();
          Scene scene = new Scene(mainWindow,600,600);
          scene.getStylesheets().add(TABLE_STYLE_PATH);
          primaryStage.setScene(scene);
        } catch (IOException e) {
          System.err.println("There was a problem loading /fxml/MainWindow.fxml");
          e.printStackTrace();
        }
    }

    public void initializeGame() {
        var tableSceneController = new TableSceneController();
        var mainWindowController = new MainWindowController();
        Dealer dealer = new Dealer();
        List<Player> allPlayers = new ArrayList<>();
        var playerName = tableSceneController.getPlayerName();
        allPlayers.add(new BlackjackPlayer(playerName));
        BlackjackGame game = new BlackjackGame(dealer, allPlayers,
            mainWindowController, tableSceneController);
        game.playGame();
    }
}

BlackjackGame.java

public class BlackjackGame implements BlackjackGameRules {

    private List<Player> playerList;
    private Deck deck;
    private Shoe shoe;
    private final TableSceneController tableSceneController;
    private final MainWindowController mainWindowController;

    public BlackjackGame(Dealer dealer, List<Player> players,
        final MainWindowController mainWindowController,
        final TableSceneController tableSceneController) {
        Objects.requireNonNull(dealer,
            "You must provide a dealer to begin the game.");
        Objects.requireNonNull(players,
            "You must provide a list of players to begin the game.");

        playerList = new ArrayList<>();

        this.tableSceneController = tableSceneController;
        this.mainWindowController = mainWindowController;

        // add dealer first for easier future access
        playerList.add(dealer);
        playerList.addAll(players);

        deck = new Deck(BlackjackGameRules.NUMBER_OF_DECKS);

        // place the shuffled deck in the shoe
        shoe = new Shoe(deck.getDeck());
    }

    public void dealInitialCards() {
        for (Player player : playerList) {
            player.getHand().addCard(shoe.dealCard());
            player.getHand().addCard(shoe.dealCard());
        }
    }

    public boolean hasValidNumberOfPlayers() {
        // this number includes the dealer
        var numPlayers = playerList.size();
        return numPlayers >= BlackjackGameRules.MIN_PLAYERS &&
            numPlayers <= BlackjackGameRules.MAX_PLAYERS;
    }

    public List<Player> getPlayers() {
        return new ArrayList<>(playerList);
    }

    public Shoe getShoe() {
        return shoe;
    }

    public void playGame() {
        dealInitialCards();

        for(Player player: playerList) {
            tableSceneController.displayInitialHand(player);
        }

    }

}

我在 TableSceneController 中的 displayIntitialHand 上收到 NullPointerException。这是简短的堆栈跟踪:

Caused by: java.lang.NullPointerException
    at blackjack.controller.TableSceneController.displayInitialHand(TableSceneController.java:35)
    at blackjack.model.BlackjackGame.playGame(BlackjackGame.java:139)
    at blackjack.controller.BlackjackMain.initializeGame(BlackjackMain.java:70)
    at blackjack.controller.BlackjackMain.start(BlackjackMain.java:44)

对于我的一生,我无法弄清楚这一点。我哪里出错了?我仔细检查了我是否在 *.fxml 文件的 fx:controller 属性中设置了控制器的名称。我还仔细检查了组件中的fx:id 属性是否正确,并且它们也正确匹配控制器中的@FXML 注释。

我对JavaFX的流程的理解是:

  1. load() 应该加载 *.fxml 文件
  2. 实例化控制器(由 .fxml 文件中的fx:controller 属性指定)
  3. 在控制器上调用无参数构造函数
  4. 设置@FXML 值(通过注入)
  5. 注册任何事件处理程序
  6. 在每个控制器上调用initialize

我的嵌套 fxml 文件有问题吗?如果是这种情况,我会认为MainWindowController.java 中的@FXML 字段也不会是null。我正在努力解决这个问题。我可以用另一双眼睛和比我更聪明的人。

提前致谢。

【问题讨论】:

  • 如果存在注入字段的控制器实例并不意味着该字段被注入到每个控制器实例。您不会期望 new ArrayList() 创建的列表包含元素,因为您将元素添加到某些 ArrayList...

标签: java javafx-8 fxml playing-cards


【解决方案1】:

花了一些时间才弄明白,但是当您为场景创建控制器时,在 initializeGame() 中您会这样做:

    var tableSceneController = new TableSceneController();
    var mainWindowController = new MainWindowController();

这意味着您正在创建控制器的新实例,而不是在 initializeMainWindow 中加载 FXML 文件时创建的实例。

为了解决这个问题,我建议创建一个类变量来保存每个控制器,然后在加载 FXML 文件时分配它们。

所以,在BlackJackMain.java,声明类变量

private TableSceneController tableSceneController;
private MainWindowController mainWindowController;

那么当你加载它们时,我可以看到你在initializeMainWindow 中加载了主窗口,所以添加

mainWindowController = loader.getController();

到 try 块,就在 loader.load 行之后。

这解决了这个场景的空指针,但我不知道你在哪里或者是否加载了表格场景 FXML,因此你没有控制器的实例可以传递给你的方法。如果您确实加载了文件,请对其应用相同的逻辑以获取该控制器的实例。

【讨论】:

  • 感谢您的尝试。我实现了您建议的更改,但是,当我稍后在代码中尝试使用类变量时,我仍然收到NullPointeException。引起:com.frijolie.blackjack.controller.BlackjackMain.initializeGame(BlackjackMain.java:71) at com.frijolie.blackjack.controller.BlackjackMain.start(BlackjackMain.java:49) 的 java.lang.NullPointerException
  • @Frijolie 我可能只是个盲人,但在initializeGame() 中,它调用tableScenController.getPlayerName(),我在TableSceneController 类中看不到它
  • 我想我解决了。我做了一些重构并从我的域对象中删除了一些依赖注入,以使过程更加简单。它似乎正在工作。现在开始在将卡片添加到列表时显示卡片。谢谢。
猜你喜欢
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-19
  • 2018-12-24
  • 2017-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多