英文:
Root cannot be null exception with multiple FXML/Controller in JavaFX
问题
以下是您提供的代码的翻译部分:
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>
<fx:include source="Workouts.fxml" fx:id="workoutsPane"/>
<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;
@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);
}
}
我已经按照您提供的代码进行了翻译,如果您有任何问题或需要进一步帮助,请随时提问。
英文:
I'm working on a JavaFX project to manage workouts. While it had one big .fxml file, and one big MainController class, it worked fine:
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);
}
}
Then I wanted to cut a piece from the WorkoutsMain.fxml. I have changed a segment in the original big .fxml file like this:
from
<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>
to
<fx:include source="Workouts.fxml" fx:id="workoutsPane"/>
Then I have created a new fxml file with its own Controller 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>
I have created an appropriate WorkoutsController class and I have changed my WorkoutsMainController like this:
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..
But from now I get Exception when I try to start my application:
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$launchApplication$159(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$launchApplication1$166(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$179(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$177(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$178(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$null$152(WinApplication.java:177)
... 1 more
Any suggestion guys, what should I do to make this work once again? With one .fxml and one Controller it worked perfectly.
Thanks
答案1
得分: 4
在你的 WorkoutsMainController
中,你正在创建一个新的 WorkoutsController
实例,而不是使用由 FXMLLoader
为你创建的实例。你创建的实例中的 @FXML
注释字段都将为 null,因此你可能在某个地方遇到了空指针异常。(你应该在控制台中看到 "Error when trying to load WorkoutsMain.fxml" 的消息。)因此,在你的 start()
方法中,anchorPane
从未被初始化,当你将空的 anchorPane
引用传递给 Scene
构造函数时会出现异常。
此外,你出于某种未知的原因,将 splitPane
和 anchorPane
做成了 static
:这样做毫无意义,而且 FXMLLoader
不会初始化静态字段。因此,控制器中的这些字段也都是 null。
如果你不压制加载 FXML 时抛出的异常,你将能够看到底层异常的实际堆栈跟踪。
修复方法如下:
-
不要将
@FXML
注释的字段设为static
。参见 https://stackoverflow.com/questions/23105433/javafx-8-compatibility-issues-fxml-static-fields -
要注入由
FXMLLoader
创建的包含 FXML 的实际控制器实例,请参阅嵌套控制器的文档。简而言之,由于你的fx:include
具有fx:id="workoutsPane"
,你应该将private final WorkoutsController workoutsController = new WorkoutsController();
替换为
@FXML private WorkoutsController workoutsPaneController;
(并在
WorkoutsMainController
的其余部分中的所有出现中将workoutsController
替换为workoutsPaneController
)。
英文:
You're creating a new WorkoutsController
instance in your WorkoutsMainController
, instead of using the one created for you by the FXMLLoader
. The @FXML
-annotated fields in the instance you create will all be null, so you are probably getting a null pointer exception somewhere. (You should be seeing the "Error when trying to load WorkoutsMain.fxml" message in the console.) Thus anchorPane
in your start()
method is never initialized and you get an exception when you pass the null anchorPane
reference to the Scene
constructor.
Note also you have (for some unknown reason) made splitPane
and anchorPane
static
: it makes no sense to do that, and the FXMLLoader
will not initialize static fields. So those fields are also null in the controller.
If you don't squash exceptions thrown by loading the FXML, you will be able to see the actual stack trace for the underlying exception.
The fixes are:
-
don't make
@FXML
-annotated fieldsstatic
. See https://stackoverflow.com/questions/23105433/javafx-8-compatibility-issues-fxml-static-fields -
To inject the actual controller instance for the included FXML that is created by the
FXMLLoader
, see Nested Controllers in the documentation. In short, since yourfx:include
hasfx:id="workoutsPane"
, you should replaceprivate final WorkoutsController workoutsController = new WorkoutsController();
with
@FXML private WorkoutsController workoutsPaneController ;
(and replace all occurrences of
workoutsController
withworkoutsPaneController
in the remainder ofWorkoutsMainController
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论