You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX中fx:id运行后消失且Rectangle空指针异常求助

JavaFX Rectangle空指针异常及fx:id消失问题解决

问题概述

编写JavaFX代码实现按键增加名为stick的Rectangle高度功能,此前按键触发文本输出正常,但为Rectangle关联fx:id后运行抛出空指针异常,且每次运行后SceneBuilder中的fx:id会消失。

错误信息

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "javafx.scene.shape.Rectangle.getHeight()" because "this.this$0.stick" is null
    at com.example.demo2@1.0-SNAPSHOT/com.example.demo2.SceneController$1.handle(SceneController.java:105)
    at com.example.demo2@1.0-SNAPSHOT/com.example.demo2.SceneController$1.handle(SceneController.java:83)
    at javafx.base@21-ea/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@21-ea/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:232)
    at javafx.base@21-ea/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:189)
    at javafx.base@21-ea/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@21-ea/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@21-ea/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21-ea/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@21-ea/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@21-ea/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@21-ea/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base@21-ea/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@21-ea/javafx.scene.Scene.processKeyEvent(Scene.java:2194)
    at javafx.graphics@21-ea/javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2715)
    at javafx.graphics@21-ea/com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:218)
    at javafx.graphics@21-ea/com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:150)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at javafx.graphics@21-ea/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$1(GlassViewEventHandler.java:250)
    at javafx.graphics@21-ea/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@21-ea/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:249)
    at javafx.graphics@21-ea/com.sun.glass.ui.View.handleKeyEvent(View.java:542)
    at javafx.graphics@21-ea/com.sun.glass.ui.View.notifyKey(View.java:966)
    at javafx.graphics@21-ea/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics@21-ea/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:185)
    at java.base/java.lang.Thread.run(Thread.java:1583)

问题控制器代码

package com.example.demo2;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.Objects;

public class SceneController {

    @FXML
    private Rectangle stick;

    @FXML
    private Stage stage;

    @FXML
    private boolean gameIsRunning = false;

    @FXML
    private javafx.scene.control.Label pausedLabel = new Label();

    @FXML
    private Scene scene;

    @FXML private Scene gameScene;

    @FXML
    private Scene pausedScene;

    @FXML
    private Parent root;

    @FXML
    private javafx.scene.control.Button closeButton;

    @FXML
    public void switchToMainMenu(ActionEvent event) throws IOException {
        root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("hello-view.fxml")));
        stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    @FXML
    public void switchToMainMenuFromGame() throws IOException {
        root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("hello-view.fxml")));
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    @FXML
    private void createBridge(KeyEvent event){
        switch (event.getCode()){
            case C -> stick.setHeight(stick.getHeight()+10.0);
        }
    }

    @FXML
    public void switchToGame(ActionEvent event) throws IOException {
        gameIsRunning = true;
        Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("game-view.fxml")));
        stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        scene = new Scene(root);
        gameScene = scene;

        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent keyEvent) {
                switch (keyEvent.getCode()){
                    case ESCAPE -> {
                        System.out.println("paused game");
                        pauseGame();
                        try {
                            switchToPausedScene();
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    case Q -> {
                        try {
                            switchToMainMenuFromGame();
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    case C -> {
                        System.out.println("creating bridge");
                        stick.setHeight(stick.getHeight()+10.0);
                    }
                    case X -> System.out.println("switching sides");
                }
            }
        });
        stage.setScene(scene);
        stage.show();
    }

    @FXML
    private void switchToPausedScene() throws IOException{
        gameIsRunning = false;
        if(null == pausedScene){
            root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("paused-menu.fxml")));
            scene = new Scene(root);
            pausedScene = scene;
        }else{
            scene = pausedScene;
        }
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent keyEvent) {
                if (Objects.requireNonNull(keyEvent.getCode()) == KeyCode.ESCAPE) {
                    try {
                        switchToGameSceneFromPaused();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        stage.setScene(scene);
        stage.show();
    }

    @FXML
    private void switchToGameSceneFromPaused() throws IOException{
        scene = gameScene;
        stage.setScene(scene);
        stage.show();
        gameIsRunning = true;
    }

    @FXML
    protected void onExitButtonClick() {
        Stage stage = (Stage) closeButton.getScene().getWindow();
        stage.close();
    }

    @FXML
    private void pauseGame(){
        gameIsRunning = false;
    }

    @FXML
    private void resumeGame(){
        gameIsRunning = true;
    }
}

game-view.fxml代码

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.shape.Rectangle?>


<AnchorPane prefHeight="966.0" prefWidth="540.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo2.SceneController">
   <children>
      <Rectangle fx:id="stick" arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="6.0" layoutX="287.0" layoutY="225.0" stroke="BLACK" strokeType="INSIDE" width="6.0" />
   </children>
</AnchorPane>

解决方案

1. 空指针异常修复

空指针的核心原因:switchToGame方法中使用FXMLLoader.load()静态方法加载game-view.fxml时,会生成一个全新的SceneController实例,而当前控制器实例的stick字段并未被这个加载过程注入(当前实例来自之前的FXML文件)。你在匿名事件处理器中访问的是当前实例的stick,自然为null。

修改switchToGame方法,获取game-view.fxml对应的控制器实例:

@FXML
public void switchToGame(ActionEvent event) throws IOException {
    gameIsRunning = true;
    // 改用非静态加载方式,获取控制器实例
    FXMLLoader loader = new FXMLLoader(getClass().getResource("game-view.fxml"));
    Parent root = loader.load();
    SceneController gameController = loader.getController();
    
    stage = (Stage)((Node)event.getSource()).getScene().getWindow();
    scene = new Scene(root);
    gameScene = scene;

    scene.setOnKeyPressed(keyEvent -> {
        switch (keyEvent.getCode()){
            case ESCAPE -> {
                System.out.println("暂停游戏");
                pauseGame();
                try {
                    switchToPausedScene();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            case Q -> {
                try {
                    switchToMainMenuFromGame();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            case C -> {
                System.out.println("创建桥梁");
                // 使用gameController的stick字段操作
                if(gameController.stick != null){
                    gameController.stick.setHeight(gameController.stick.getHeight()+10.0);
                }
            }
            case X -> System.out.println("切换 sides");
        }
    });
    stage.setScene(scene);
    stage.show();
}

2. fx:id消失问题修复

  • 移除控制器中所有@FXML字段的手动初始化,比如private Label pausedLabel = new Label();改为private Label pausedLabel;,手动初始化会覆盖FXML注入的值,也会导致SceneBuilder识别异常。
  • 只给需要FXML注入的字段添加@FXML注解,stage、scene、gameIsRunning等手动赋值的字段不需要加@FXML。
  • 确保game-view.fxml中fx:controller属性正确指向com.example.demo2.SceneController,保存FXML后重新打开SceneBuilder,fx:id就不会消失了。

内容的提问来源于stack exchange,提问作者Surat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 06:18:11