简体   繁体   English

使用Scene Builder 8.0在JavaFX中使用setCenter()方法获取空指针异常

[英]Getting Null Pointer Exception with setCenter() method in JavaFX with Scene Builder 8.0

I created a small program to reproduce the error I am getting in my actual project. 我创建了一个小程序来重现我在实际项目中遇到的错误。 I have a controller class called MainWindow.java that is responsible for two .fxml files: MainWindow.fxml and AnchorTest.fxml . 我有一个名为MainWindow.java的控制器类,它负责两个.fxml文件: MainWindow.fxmlAnchorTest.fxml

Code for the controller class: 控制器类的代码:

package projecterror.controller;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class MainWindow extends Application {

    private Stage primaryStage;
    private BorderPane mainWindow;

    @FXML
    Menu menuFile, menuAnalysis;
    @FXML
    MenuItem menuNew;


    @FXML
    private void initialize() {
        menuNew.setOnAction((event) -> {
            try {
                FXMLLoader loader = new FXMLLoader();
                loader.setLocation(getClass().getResource("../view/AnchorTest.fxml"));
                AnchorPane anchorTest = (AnchorPane) loader.load();
                mainWindow.setCenter(anchorTest);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("Test Project");

        initMainWindow();

    }

    public void initMainWindow() {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource("../view/MainWindow.fxml"));
            mainWindow = (BorderPane) loader.load();

            Scene scene = new Scene(mainWindow);
            primaryStage.setScene(scene);

            //Fullscreen
            Screen screen = Screen.getPrimary();
            Rectangle2D bounds = screen.getVisualBounds();
            primaryStage.setX(bounds.getMinX());
            primaryStage.setY(bounds.getMinY());
            primaryStage.setWidth(bounds.getWidth());
            primaryStage.setHeight(bounds.getHeight());

            primaryStage.setResizable(false);
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

MainWindow.fxml is a BorderPane that has a MenuBar on top . MainWindow.fxmlBorderPanetop具有MenuBar

Code: 码:

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

<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.SeparatorMenuItem?>
<?import javafx.scene.layout.BorderPane?>

<BorderPane style="-fx-background-color: #DCDCDC;" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="projecterror.controller.MainWindow">
    <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu fx:id="menuFile" mnemonicParsing="false" text="File">
            <items>
                  <Menu fx:id="menuAnalysis" mnemonicParsing="false" text="Analysis">
                    <items>
                      <MenuItem fx:id="menuNew" mnemonicParsing="false" text="New" />
                    </items>
                  </Menu>
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </top>
</BorderPane>


AnchorTest.fxml is an AnchorPane that has as children two AnchorPane . AnchorTest.fxml是一个AnchorPane ,具有两个子AnchorPane

Code: 码:

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

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane xmlns:fx="http://javafx.com/fxml/1">
    <children>
            <AnchorPane layoutX="6.0" layoutY="450.0" />
            <AnchorPane layoutX="6.0" layoutY="569.0" prefHeight="116.0" prefWidth="730.0">
                  <Button layoutX="6.0"   layoutY="26.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="102.0" text="Button 1" />
                  <Button layoutX="115.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 2" />
                  <Button layoutX="226.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 3" />
                  <Button layoutX="115.0" layoutY="75.0" mnemonicParsing="false" prefWidth="102.0" text="Button 4" />
                  <Button layoutX="383.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 5" />
                  <Button layoutX="491.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 6" />
                  <Button layoutX="600.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 7" />
                  <Button layoutX="491.0" layoutY="75.0" mnemonicParsing="false" prefWidth="102.0" text="Button 8" />
                  <Button layoutX="600.0" layoutY="75.0" mnemonicParsing="false" prefWidth="102.0" text="Button 9" />
                  <TextField alignment="TOP_CENTER" layoutX="341.0" layoutY="26.0" prefHeight="25.0" prefWidth="28.0" text="10" />
            </AnchorPane>
         </children>
</AnchorPane>


When on MainWindow.fxml , after I click on the menu File > Analysis > New , it is supposed to place AnchorTest.fxml content into MainWindow.fxml center. MainWindow.fxml ,单击菜单File > Analysis > New ,应该将AnchorTest.fxml内容放入MainWindow.fxml中心。 However, I get the following stack trace error: 但是,我得到以下堆栈跟踪错误:

 Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
        at projecterror.controller.MainWindow.lambda$0(MainWindow.java:35)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
        at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
        at javafx.event.Event.fireEvent(Event.java:198)
        at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
        at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
        at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
        at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
        at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
        at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
        at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
        at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
        at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
        at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
        at javafx.event.Event.fireEvent(Event.java:198)
        at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
        at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
        at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
        at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:380)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:294)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:416)
        at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
        at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:415)
        at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
        at com.sun.glass.ui.View.notifyMouse(View.java:937)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
        at java.lang.Thread.run(Thread.java:745)

The line that is flagging the NPE is: 标记NPE的行是:

mainWindow.setCenter(anchorTest);

I have read other questions similar to this but mostly all of their problems was regarding the path of the fxml files. 我已经阅读了与此类似的其他问题,但大多数问题都与fxml文件的路径有关。 I firmly believe that is not the problem since in my actual project I use the same type of path for other views and I don't have problem so far. 我坚信这不是问题,因为在我的实际项目中,我对其他视图使用相同类型的路径,到目前为止,我还没有遇到任何问题。

I have uploaded the project here in case someone wants to run the program. 如果有人要运行该程序,我已经在这里上传了该项目。

Project structure is as it follows: 项目结构如下:

项目结构

Thanks in advance and any help would be greatly appreciated! 在此先感谢您,任何帮助将不胜感激!

When you launch a JavaFX application, an instance of your application class is created, and (among other things that happen), the start() method is invoked on that Application instance on the FX Application Thread. 当您启动JavaFX应用程序时,将创建您的应用程序类的实例,并且(在其他情况下)将在FX Application线程上的该Application实例上调用start()方法。

When you load an FXML file, if the FXML file specifies a controller class, an instance of that controller class is created, the @FXML -annotated fields are injected into that instance, and the initialize() method is called. 加载FXML文件时,如果FXML文件指定了控制器类, @FXML创建该控制器类的实例,将@FXML字段注入该实例中,并调用initialize()方法。 All this happens during the call to FXMLLoader.load() . 所有这些都在调用FXMLLoader.load()过程中发生。

Consequently, in your code, you end up with two different instances of MainWindow . 因此,在您的代码中,您最终得到MainWindow两个不同实例。 The start() method is invoked on one instance, which initializes the mainWindow field, and the initialize() method is invoked on the other instance. 在一个实例上调用start()方法,该实例将初始化mainWindow字段,在另一个实例上调用initialize()方法。 Since mainWindow was never initialized in that second instance, you get a null pointer exception when you try to dereference it in the initialize() method. 由于mainWindow从未在第二个实例中初始化,因此当您尝试在initialize()方法中对其进行取消引用时,会出现空指针异常。

Separate the controller class from the Application class. 将控制器类与Application类分开。 The Application class should do nothing other than manage the lifecycle of the application as a whole (typically, it should implement the code needed to start the application): Application类除了管理整个应用程序的生命周期外别无所求(通常,它应实现启动应用程序所需的代码):

package projecterror.controller;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class MainWindow extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Test Project");

        initMainWindow(primaryStage);

    }

    public void initMainWindow(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource("../view/MainWindow.fxml"));
            BorderPane mainWindow = loader.load();

            Scene scene = new Scene(mainWindow);
            primaryStage.setScene(scene);

            //Fullscreen
            Screen screen = Screen.getPrimary();
            Rectangle2D bounds = screen.getVisualBounds();
            primaryStage.setX(bounds.getMinX());
            primaryStage.setY(bounds.getMinY());
            primaryStage.setWidth(bounds.getWidth());
            primaryStage.setHeight(bounds.getHeight());

            primaryStage.setResizable(false);
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
package projecterror.controller;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;

public class MainWindowController {

    @FXML
    private BorderPane mainWindow;

    @FXML
    Menu menuFile, menuAnalysis;
    @FXML
    MenuItem menuNew;


    @FXML
    private void initialize() {
        menuNew.setOnAction((event) -> {
            try {
                FXMLLoader loader = new FXMLLoader();
                loader.setLocation(getClass().getResource("../view/AnchorTest.fxml"));
                AnchorPane anchorTest = (AnchorPane) loader.load();
                mainWindow.setCenter(anchorTest);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }



}

In order for the controller to access the BorderPane , you need to inject it from the FXML (note the annotation on it in the controller code above). 为了使控制器访问BorderPane ,您需要从FXML注入它(请注意上面的控制器代码中的注释)。 Add an fx:id to the element in the FXML file: fx:id添加到FXML文件中的元素:

<BorderPane style="-fx-background-color: #DCDCDC;" fx:id="mainWindow"
    xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" 
    fx:controller="projecterror.controller.MainWindowController">

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM