根节点不能为空异常,在JavaFX中有多个FXML/Controller。

huangapple go评论68阅读模式
英文:

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:

&lt;AnchorPane id=&quot;AnchorPane&quot; fx:id=&quot;anchorPane&quot; prefHeight=&quot;900.0&quot; prefWidth=&quot;1600.0&quot; xmlns=&quot;http://javafx.com/javafx/8&quot; xmlns:fx=&quot;http://javafx.com/fxml/1&quot; fx:controller=&quot;workouts.WorkoutsMainController&quot;&gt;
    &lt;children&gt;
        &lt;SplitPane fx:id=&quot;splitPane&quot; dividerPositions=&quot;0.15&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;0.0&quot;&gt;
            &lt;items&gt;
                &lt;AnchorPane&gt;
                    &lt;children&gt;
                        &lt;StackPane fx:id=&quot;menuPane&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;0.0&quot;/&gt;
                    &lt;/children&gt;
                &lt;/AnchorPane&gt;
                &lt;AnchorPane&gt;
                    &lt;children&gt;
                        &lt;Pane fx:id=&quot;statisticsPane&quot; visible=&quot;false&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;130.0&quot;/&gt;
                        &lt;Pane fx:id=&quot;workoutsPane&quot; visible=&quot;false&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;130.0&quot;&gt;
                        //... other FX elements
                        &lt;/Pane&gt;
                        &lt;Pane fx:id=&quot;calendarPane&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;130.0&quot;/&gt;
                        &lt;StackPane fx:id=&quot;logoPane&quot; onMouseClicked=&quot;#setLogo&quot; AnchorPane.bottomAnchor=&quot;778.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;0.0&quot;/&gt;
                    &lt;/children&gt;
                &lt;/AnchorPane&gt;
            &lt;/items&gt;
        &lt;/SplitPane&gt;
    &lt;/children&gt;
&lt;/AnchorPane&gt;

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&lt;Pane&gt; panes = new ArrayList&lt;&gt;();
    @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 = &quot;WorkoutsMain.fxml&quot;;
    @Override
    public void start(Stage stage) {       
        try {
            anchorPane = FXMLLoader.load(getClass().getResource(fxml));
        } catch (IOException ex) {
            System.out.println(&quot;Error when trying to load &quot; + fxml);
          }
        Scene scene = new Scene(anchorPane);
        stage.setScene(scene);
        stage.getIcons().add(new Image(getClass().getResourceAsStream(&quot;/calendarIconWhite.png&quot;)));
        stage.setTitle(&quot;THE WORKOUT CALENDAR 1.7.0 by hazazs&#174;&quot;);
        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

&lt;Pane fx:id=&quot;workoutsPane&quot; visible=&quot;false&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;130.0&quot;&gt;
    //... other FX elements
&lt;/Pane&gt;

to

&lt;fx:include source=&quot;Workouts.fxml&quot; fx:id=&quot;workoutsPane&quot;/&gt;

Then I have created a new fxml file with its own Controller class:
Workouts.fxml:

&lt;Pane fx:id=&quot;workoutsPane&quot; visible=&quot;false&quot; AnchorPane.bottomAnchor=&quot;0.0&quot; AnchorPane.leftAnchor=&quot;0.0&quot; AnchorPane.rightAnchor=&quot;0.0&quot; AnchorPane.topAnchor=&quot;130.0&quot; xmlns:fx=&quot;http://javafx.com/fxml/1&quot; fx:controller=&quot;workouts.WorkoutsController&quot;&gt;
// other FX elements
&lt;/Pane&gt;

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&lt;Pane&gt; panes = new ArrayList&lt;&gt;();
    @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.&lt;init&gt;(Scene.java:336)
	at javafx.scene.Scene.&lt;init&gt;(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 构造函数时会出现异常。

此外,你出于某种未知的原因,将 splitPaneanchorPane 做成了 static:这样做毫无意义,而且 FXMLLoader 不会初始化静态字段。因此,控制器中的这些字段也都是 null。

如果你不压制加载 FXML 时抛出的异常,你将能够看到底层异常的实际堆栈跟踪。

修复方法如下:

  1. 不要将 @FXML 注释的字段设为 static。参见 https://stackoverflow.com/questions/23105433/javafx-8-compatibility-issues-fxml-static-fields

  2. 要注入由 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:

  1. don't make @FXML-annotated fields static. See https://stackoverflow.com/questions/23105433/javafx-8-compatibility-issues-fxml-static-fields

  2. 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 your fx:include has fx:id=&quot;workoutsPane&quot;, you should replace

     private final WorkoutsController workoutsController = new WorkoutsController();
    

    with

     @FXML private WorkoutsController workoutsPaneController ;
    

    (and replace all occurrences of workoutsController with workoutsPaneController in the remainder of WorkoutsMainController).

huangapple
  • 本文由 发表于 2020年9月1日 22:00:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/63689241.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定