我所有的@FXML引用都是null

时间:2018-05-20 04:47:07

标签: java javafx-8 fxml playing-cards

我试图在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. 调用控制器上的no-arg构造函数
  4. 设置@FXML值(通过注入)
  5. 注册任何事件处理程序
  6. 在每个控制器上调用initialize
  7. 我的嵌套fxml文件有问题吗?如果是这种情况,我认为MainWindowController.java中的@FXML字段也不会是null。我正在努力解决这个问题。我可以使用另一组眼睛和比我更聪明的人。

    提前致谢。

1 个答案:

答案 0 :(得分: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,因此你没有控制器的实例传递给你的方法。如果您确实加载了该文件,请对其应用相同的逻辑以获取该控制器的实例。