IntelliJ 2018.1与Scene Builder 9.0.1下JavaFX切换场景空指针问题
解决JavaFX中Controller调用Main切换场景时的NullPointerException问题
你的空指针报错原因非常清晰:你在Controller里手动创建了一个新的Main实例,但这个实例的primaryStage根本没有被初始化。JavaFX的start()方法是由FX应用启动器自动调用的,你自己new出来的Main对象不会执行start()方法,所以它的primaryStage变量还是null,调用changeScene()自然就触发空指针了。
下面给你几种可行的解决方案,按推荐程度排序:
方案1:从当前控件获取Stage(最推荐,解耦)
不需要依赖Main类,直接从触发点击事件的按钮获取它所在的Stage,这种方式让Controller和Main完全解耦,代码更灵活:
修改Controller的buttonNewProductOnMouseClicked方法:
public void buttonNewProductOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) { buttonNewProduct.setText("You have clicked on me!"); // 从按钮获取当前所在的Stage Stage stage = (Stage) buttonNewProduct.getScene().getWindow(); try { // 加载目标FXML Parent pane = FXMLLoader.load(getClass().getResource("new_product/new_product.fxml")); Scene newScene = new Scene(pane); stage.setScene(newScene); } catch (IOException e) { e.printStackTrace(); } }
方案2:给Controller传递Stage引用
在加载主FXML的时候,把Main中初始化好的primaryStage传递给Controller:
修改Main.java的start方法
@Override public void start(Stage primaryStage) throws Exception{ this.primaryStage = primaryStage; // 改用FXMLLoader实例加载,方便获取Controller FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml")); Parent root = loader.load(); // 获取Controller实例并传递Stage Controller controller = loader.getController(); controller.setStage(primaryStage); Scene primaryScene = new Scene(root, 300, 275); primaryStage.setScene(primaryScene); primaryStage.show(); }
修改Controller.java
添加Stage成员变量和setter方法,然后直接用这个Stage切换场景:
package sample; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.stage.Stage; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import java.io.IOException; public class Controller { @FXML private Button buttonProducts; @FXML private Button buttonNewProduct; private Stage stage; // 接收Main传递的Stage public void setStage(Stage stage) { this.stage = stage; } public void buttonProductsOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) { buttonProducts.setText("You have clicked on me!"); } public void buttonNewProductOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) { buttonNewProduct.setText("You have clicked on me!"); try { Parent pane = FXMLLoader.load(getClass().getResource("new_product/new_product.fxml")); Scene newScene = new Scene(pane); stage.setScene(newScene); } catch (IOException e) { e.printStackTrace(); } } }
方案3:将Main设为单例(不推荐,强耦合)
如果你一定要通过Main类来切换场景,可以把Main做成单例,确保Controller拿到的是启动时的那个实例:
修改Main.java
package sample; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.IOException; public class Main extends Application { private static Main instance; private Stage primaryStage; @Override public void start(Stage primaryStage) throws Exception{ instance = this; // 保存启动时的实例 this.primaryStage = primaryStage; Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); Scene primaryScene = new Scene(root, 300, 275); primaryStage.setScene(primaryScene); primaryStage.show(); } // 提供获取单例的静态方法 public static Main getInstance() { return instance; } public void changeScene(String fxml){ Parent pane = null; try { pane = FXMLLoader.load(getClass().getResource(fxml)); } catch (IOException e) { e.printStackTrace(); } // 加非空判断,避免FXML加载失败导致的空指针 if(pane != null) { Scene scene = new Scene(pane); primaryStage.setScene(scene); } } public static void main(String[] args) { launch(args); } }
修改Controller的调用
public void buttonNewProductOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) { buttonNewProduct.setText("You have clicked on me!"); Main.getInstance().changeScene("new_product/new_product.fxml"); }
额外注意事项
- 确认
new_product.fxml的路径正确:你写的"new_product/new_product.fxml"需要对应项目中src/sample/new_product/目录下的文件(编译后会在classpath的对应位置)。 - 加载FXML时建议添加异常处理和非空判断,避免因为文件找不到或加载失败导致后续错误。
内容的提问来源于stack exchange,提问作者Sigma
相关产品推荐
相关产品推荐

