根不能为 JavaFX 中多个 FXML/Controller 的空异常
Root cannot be null exception with multiple FXML/Controller in JavaFX
我正在开发一个管理锻炼的 JavaFX 项目。虽然它有一个大的 .fxml 文件和一个大的 MainController class,但它工作正常:
WorkoutsMain.fxml:
<AnchorPane id="AnchorPane" fx:id="anchorPane" prefHeight="900.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="workouts.WorkoutsMainController">
<children>
<SplitPane fx:id="splitPane" dividerPositions="0.15" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane>
<children>
<StackPane fx:id="menuPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"/>
</children>
</AnchorPane>
<AnchorPane>
<children>
<Pane fx:id="statisticsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0"/>
<Pane fx:id="workoutsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0">
//... other FX elements
</Pane>
<Pane fx:id="calendarPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0"/>
<StackPane fx:id="logoPane" onMouseClicked="#setLogo" AnchorPane.bottomAnchor="778.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"/>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
WorkoutsMainController.java:
public class WorkoutsMainController implements Initializable, SplitPaneDividerController {
private final DataBase dataBase = new DataBase();
private boolean first = true;
private String logoName;
@FXML
private StackPane logoPane, menuPane;
private final List<Pane> panes = new ArrayList<>();
@FXML
private Pane calendarPane, workoutsPane, statisticsPane;
@FXML
private SplitPane splitPane;
@FXML
private AnchorPane anchorPane;
// methods and functions
@Override
public void initialize(URL url, ResourceBundle rb) {
setLogo();
disableSplitPaneDivider(splitPane, 0.1525);
setUpMainMenu();
}
}
MainClass.java:
public class MainClass extends Application {
private Parent anchorPane;
private final String fxml = "WorkoutsMain.fxml";
@Override
public void start(Stage stage) {
try {
anchorPane = FXMLLoader.load(getClass().getResource(fxml));
} catch (IOException ex) {
System.out.println("Error when trying to load " + fxml);
}
Scene scene = new Scene(anchorPane);
stage.setScene(scene);
stage.getIcons().add(new Image(getClass().getResourceAsStream("/calendarIconWhite.png")));
stage.setTitle("THE WORKOUT CALENDAR 1.7.0 by hazazs®");
stage.setResizable(false);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
然后我想从WorkoutsMain.fxml上剪一块。我已经像这样更改了原始大 .fxml 文件中的一个段:
来自
<Pane fx:id="workoutsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0">
//... other FX elements
</Pane>
到
<fx:include source="Workouts.fxml" fx:id="workoutsPane"/>
然后我创建了一个新的 fxml 文件,它有自己的控制器 class:
Workouts.fxml:
<Pane fx:id="workoutsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="workouts.WorkoutsController">
// other FX elements
</Pane>
我已经创建了一个合适的 WorkoutsController class 并且我已经像这样更改了我的 WorkoutsMainController:
public class WorkoutsMainController implements Initializable, SplitPaneDividerController {
protected static final DataBase dataBase = new DataBase();
private final WorkoutsController workoutsController = new WorkoutsController();
private boolean first = true;
private String logoName;
@FXML
private StackPane logoPane, menuPane;
private final List<Pane> panes = new ArrayList<>();
@FXML
private Pane calendarPane, workoutsPane, statisticsPane;
@FXML
protected static SplitPane splitPane;
@FXML
protected static AnchorPane anchorPane;
// and so on..
但是从现在开始,当我尝试启动我的应用程序时出现异常:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication9(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException: Root cannot be null
at javafx.scene.Scene.<init>(Scene.java:336)
at javafx.scene.Scene.<init>(Scene.java:194)
at workouts.MainClass.start(MainClass.java:23)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication16(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait9(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null7(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater8(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null2(WinApplication.java:177)
... 1 more
大家有什么建议吗,我应该怎么做才能让这项工作再次成功?使用一个 .fxml 和一个 Controller,它工作得很好。
谢谢
您正在 WorkoutsMainController
中创建一个新的 WorkoutsController
实例,而不是使用 FXMLLoader
为您创建的实例。您创建的实例中的 @FXML
-annotated 字段将全部为空,因此您可能在某处遇到空指针异常。 (您应该会在控制台中看到“尝试加载 WorkoutsMain.fxml 时出错”消息。)因此,您的 start()
方法中的 anchorPane
永远不会初始化,并且当您通过对 Scene
构造函数的空 anchorPane
引用。
另请注意,您(出于某种未知原因)制作了 splitPane
和 anchorPane
static
:这样做没有任何意义,FXMLLoader
也不会初始化静态字段。所以这些字段在控制器中也是空的。
如果您不压缩通过加载 FXML 抛出的异常,您将能够看到底层异常的实际堆栈跟踪。
修复是:
不要制作 @FXML
-注释字段 static
。参见 javafx 8 compatibility issues - FXML static fields
要为 FXMLLoader
创建的包含的 FXML 注入实际控制器实例,请参阅文档中的 Nested Controllers。简而言之,既然你的 fx:include
有 fx:id="workoutsPane"
,你应该替换
private final WorkoutsController workoutsController = new WorkoutsController();
与
@FXML private WorkoutsController workoutsPaneController ;
(并在 WorkoutsMainController
的其余部分中将所有出现的 workoutsController
替换为 workoutsPaneController
。
我正在开发一个管理锻炼的 JavaFX 项目。虽然它有一个大的 .fxml 文件和一个大的 MainController class,但它工作正常:
WorkoutsMain.fxml:
<AnchorPane id="AnchorPane" fx:id="anchorPane" prefHeight="900.0" prefWidth="1600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="workouts.WorkoutsMainController">
<children>
<SplitPane fx:id="splitPane" dividerPositions="0.15" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane>
<children>
<StackPane fx:id="menuPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"/>
</children>
</AnchorPane>
<AnchorPane>
<children>
<Pane fx:id="statisticsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0"/>
<Pane fx:id="workoutsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0">
//... other FX elements
</Pane>
<Pane fx:id="calendarPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0"/>
<StackPane fx:id="logoPane" onMouseClicked="#setLogo" AnchorPane.bottomAnchor="778.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"/>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
WorkoutsMainController.java:
public class WorkoutsMainController implements Initializable, SplitPaneDividerController {
private final DataBase dataBase = new DataBase();
private boolean first = true;
private String logoName;
@FXML
private StackPane logoPane, menuPane;
private final List<Pane> panes = new ArrayList<>();
@FXML
private Pane calendarPane, workoutsPane, statisticsPane;
@FXML
private SplitPane splitPane;
@FXML
private AnchorPane anchorPane;
// methods and functions
@Override
public void initialize(URL url, ResourceBundle rb) {
setLogo();
disableSplitPaneDivider(splitPane, 0.1525);
setUpMainMenu();
}
}
MainClass.java:
public class MainClass extends Application {
private Parent anchorPane;
private final String fxml = "WorkoutsMain.fxml";
@Override
public void start(Stage stage) {
try {
anchorPane = FXMLLoader.load(getClass().getResource(fxml));
} catch (IOException ex) {
System.out.println("Error when trying to load " + fxml);
}
Scene scene = new Scene(anchorPane);
stage.setScene(scene);
stage.getIcons().add(new Image(getClass().getResourceAsStream("/calendarIconWhite.png")));
stage.setTitle("THE WORKOUT CALENDAR 1.7.0 by hazazs®");
stage.setResizable(false);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
然后我想从WorkoutsMain.fxml上剪一块。我已经像这样更改了原始大 .fxml 文件中的一个段:
来自
<Pane fx:id="workoutsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0">
//... other FX elements
</Pane>
到
<fx:include source="Workouts.fxml" fx:id="workoutsPane"/>
然后我创建了一个新的 fxml 文件,它有自己的控制器 class:
Workouts.fxml:
<Pane fx:id="workoutsPane" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="130.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="workouts.WorkoutsController">
// other FX elements
</Pane>
我已经创建了一个合适的 WorkoutsController class 并且我已经像这样更改了我的 WorkoutsMainController:
public class WorkoutsMainController implements Initializable, SplitPaneDividerController {
protected static final DataBase dataBase = new DataBase();
private final WorkoutsController workoutsController = new WorkoutsController();
private boolean first = true;
private String logoName;
@FXML
private StackPane logoPane, menuPane;
private final List<Pane> panes = new ArrayList<>();
@FXML
private Pane calendarPane, workoutsPane, statisticsPane;
@FXML
protected static SplitPane splitPane;
@FXML
protected static AnchorPane anchorPane;
// and so on..
但是从现在开始,当我尝试启动我的应用程序时出现异常:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication9(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException: Root cannot be null
at javafx.scene.Scene.<init>(Scene.java:336)
at javafx.scene.Scene.<init>(Scene.java:194)
at workouts.MainClass.start(MainClass.java:23)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication16(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait9(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null7(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater8(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null2(WinApplication.java:177)
... 1 more
大家有什么建议吗,我应该怎么做才能让这项工作再次成功?使用一个 .fxml 和一个 Controller,它工作得很好。
谢谢
您正在 WorkoutsMainController
中创建一个新的 WorkoutsController
实例,而不是使用 FXMLLoader
为您创建的实例。您创建的实例中的 @FXML
-annotated 字段将全部为空,因此您可能在某处遇到空指针异常。 (您应该会在控制台中看到“尝试加载 WorkoutsMain.fxml 时出错”消息。)因此,您的 start()
方法中的 anchorPane
永远不会初始化,并且当您通过对 Scene
构造函数的空 anchorPane
引用。
另请注意,您(出于某种未知原因)制作了 splitPane
和 anchorPane
static
:这样做没有任何意义,FXMLLoader
也不会初始化静态字段。所以这些字段在控制器中也是空的。
如果您不压缩通过加载 FXML 抛出的异常,您将能够看到底层异常的实际堆栈跟踪。
修复是:
不要制作
@FXML
-注释字段static
。参见 javafx 8 compatibility issues - FXML static fields要为
FXMLLoader
创建的包含的 FXML 注入实际控制器实例,请参阅文档中的 Nested Controllers。简而言之,既然你的fx:include
有fx:id="workoutsPane"
,你应该替换private final WorkoutsController workoutsController = new WorkoutsController();
与
@FXML private WorkoutsController workoutsPaneController ;
(并在
WorkoutsMainController
的其余部分中将所有出现的workoutsController
替换为workoutsPaneController
。