使用Scene Builder 8.0在JavaFX中使用setCenter()方法获取Null Pointer Exception

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Scene Builder 8.0在JavaFX中使用setCenter()方法获取Null Pointer Exception相关的知识,希望对你有一定的参考价值。

我创建了一个小程序来重现我在实际项目中得到的错误。我有一个名为MainWindow.java的控制器类,它负责两个.fxml文件:MainWindow.fxmlAnchorTest.fxml。 控制器类的代码:

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.fxmlBorderPane,在MenuBar上有top。 码:

<?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是一个AnchorPane,有孩子两个AnchorPane

码:

<?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>

MainWindow.fxml上,点击菜单File > Analysis > New后,它应该将AnchorTest.fxml内容放入MainWindow.fxml中心。但是,我收到以下堆栈跟踪错误:

 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)

标记NPE的行是:

mainWindow.setCenter(anchorTest);

我已经阅读了与此类似的其他问题,但大部分问题都与fxml文件的路径有关。我坚信这不是问题,因为在我的实际项目中,我对其他视图使用相同类型的路径,到目前为止我没有问题。 我上传了项目here以防有人想要运行该程序。 项目结构如下: Project structure 在此先感谢,任何帮助将不胜感激!

答案

当您启动JavaFX应用程序时,会创建一个应用程序类的实例,并且(在其他情况下)会在FX应用程序线程上的start()实例上调用Application方法。

加载FXML文件时,如果FXML文件指定了控制器类,则创建该控制器类的实例,将@FXML-annotated字段注入该实例,并调用initialize()方法。所有这些都发生在对FXMLLoader.load()的调用期间。

因此,在您的代码中,您最终会得到两个不同的MainWindow实例。在一个实例上调用start()方法,该实例初始化mainWindow字段,并在另一个实例上调用initialize()方法。由于mainWindow从未在第二个实例中初始化,因此当您尝试在initialize()方法中取消引用时,会得到空指针异常。

将控制器类与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();
            }
        });
    }



}

为了让控制器访问BorderPane,您需要从FXML中注入它(请注意上面控制器代码中的注释)。将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">

以上是关于使用Scene Builder 8.0在JavaFX中使用setCenter()方法获取Null Pointer Exception的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX的Scene Builder的奇怪屏幕大小

JavaFX Scene Builder 使用基础

JavaFX之Scene Builder详细使用说明之组件篇——菜单组件

通过 Scene Builder 将 VirtualizedScrollPane 添加到 InlineCssTextArea 或任何相关的 GenericStyledArea

JavaFX Scene Builder 拖放的 IntelliJ IDEA 插件停止工作

Netbeans 中 Scene Builder 的配置错误