Loader instantiation throws nullpointer in JavaFX

2019-03-04 02:01发布

问题:

I have declared two fxml files and a controller for each one: rootlayoutcontroller is a controller for rootlayout.fxml and overviewcontroller is a controller for overview.fxml

rootlayout has menu bar with file open item, and overviewcontroller has a Draw button.

I have another class called DataStructure. I want to open a file and send the path to overviewcontroller. Then when I click Draw the constructor of DartaStructure shall get instantiated, with the path as parameter. But as soon as I click open, on file chooser open dialog, the program throws an nullpointer for in handleOpen() method. Sometimes I get Source not found!

In a main class I wrap overview in rootlayout and show the stage.

RootLayoutController:

public class RootLayoutController {
private final Model model;

public RootLayoutController(Model model){
this.model  = model;
}

/**
 * Opens a FileChooser to let the user select a .z3lgm file to load.
 */
@FXML
private void handleOpen() {

    FileChooser fileChooser = new FileChooser();

    // Set extension filter
    FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
            "3lgm2 files (*.z3lgm)", "*z3lgm");
    fileChooser.getExtensionFilters().add(extFilter);

    // Show open file dialog
    File file = fileChooser.showOpenDialog(main.getPrimaryStage());     
    if (file != null) {
        path = file.toString();     
        model.setText(path);//error


    }

}
}

OverViewController:

public class OverViewController implements Initializable {
private final Model model;

public OverViewController(Model model){
    this.model  = model;
}
public void treeTableDraw(ActionEvent event) {

   String p = model.getText();
     new Controller(p);
    drawTable();
    numberOfFunctions = dc.getFuncAll().size();
    numberOfOrganizations = dc.getSortedAssignedOrg().size();
    funcLabel.setText(numberOfFunctions + "");
    orgLabel.setText(numberOfOrganizations + "");
    btnDraw.setDisable(true);

}
}

Error stack:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1770)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1653)
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$341(ContextMenuContent.java:1358)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$$Lambda$326/15872584.handle(Unknown Source)
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:3758)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3486)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2495)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:350)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$350(GlassViewEventHandler.java:385)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda$209/5704663.get(Unknown Source)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:404)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:384)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:927)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$null$48(GtkApplication.java:139)
at com.sun.glass.ui.gtk.GtkApplication$$Lambda$41/19140780.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
 Caused by: 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:497)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1767)
... 46 more
Caused by: java.lang.NullPointerException
at view.RootLayoutController.handleOpen(RootLayoutController.java:174)
... 56 more

Main Class:

public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private Model model = new Model;

// private ObservableList<DataConstructor> treeTableData =
// FXCollections.observableArrayList();

@Override
public void start(Stage primaryStage) {

    this.primaryStage = primaryStage;
    this.primaryStage.setTitle("IT-Saturation");
    initRootLayout();
    showOverView();

}

private void showOverView() {
    try {
        FXMLLoader loader = new FXMLLoader();

        loader.setLocation(Main.class.getResource("/view/OverView.fxml"));
        loader.setController(new OverViewController(model));
    //  Parent firstUI = loader.load();
        AnchorPane overView = (AnchorPane) loader.load();
        rootLayout.setCenter(overView);
        // OverViewController controller = loader.getController();
        // controller.setMainApp(this);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void initRootLayout() {
    try {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Main.class.getResource("/view/RootLayout.fxml"));
        loader.setController(new RootLayoutController(model));

        rootLayout = (BorderPane) loader.load();
        // show scene containing the root layout
        Scene scene = new Scene(rootLayout);
        scene.getStylesheets().add(
                getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        // gives controller access to main
        RootLayoutController controller = loader.getController();
        controller.setMainApp(this);
        primaryStage.show();

    } catch (IOException e) {

        e.printStackTrace();
    }


}

/**
 * Returns the main stage.
 * 
 * @return primaryStage
 */
public Stage getPrimaryStage() {
    return primaryStage;
}

public static void main(String[] args) {
    launch(args);
}





public void showMostComputerizedStatistics() {
    try {
        // Load the fxml file and create a new stage for the popup.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(Main.class
                .getResource("view/BirthdayStatistics.fxml"));
        AnchorPane page = (AnchorPane) loader.load();
        Stage dialogStage = new Stage();
        dialogStage.setTitle("Birthday Statistics");
        dialogStage.initModality(Modality.WINDOW_MODAL);
        dialogStage.initOwner(primaryStage);
        Scene scene = new Scene(page);
        dialogStage.setScene(scene);

        // Set the persons into the controller.
        MostComputerizedController controller = loader.getController();

        dialogStage.show();

    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

This is RootLayout fxml:

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

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

<BorderPane prefHeight="500.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
   <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu text="_File">
            <items>
                  <MenuItem onAction="#handleOpen" text="_Open" />
                  <MenuItem mnemonicParsing="false" onAction="#handleExportPic" text="Export as picture" />
                  <MenuItem mnemonicParsing="false" text=" Save as" />
                  <MenuItem mnemonicParsing="false" onAction="#doPrint" text="Print" />
              <MenuItem onAction="#handleExit" text="E_xit">
                     <accelerator>
                        <KeyCodeCombination alt="UP" code="X" control="DOWN" meta="UP" shift="UP" shortcut="UP" />
                     </accelerator></MenuItem>
            </items>
          </Menu>
            <Menu mnemonicParsing="false" onAction="#handleMostComputerizedStatistics" text="Statistics">
              <items>
                <MenuItem mnemonicParsing="false" onAction="#handleMostComputerizedStatistics" text="Show Statistics" />
              </items>
            </Menu>
          <Menu mnemonicParsing="false" text="Help">
            <items>
              <MenuItem mnemonicParsing="false" text="About" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </top>
</BorderPane>

And this is overview fxml:

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

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

<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
   <children>
      <SplitPane dividerPositions="0.2391304347826087" layoutX="62.0" layoutY="52.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <items>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" SplitPane.resizableWithParent="false">
               <children>
                  <Button fx:id="btnDraw" layoutX="24.0" layoutY="297.0" mnemonicParsing="false" onAction="#treeTableDraw" text="Draw" AnchorPane.leftAnchor="22.0" AnchorPane.rightAnchor="36.0" />
                  <Label fx:id="funcLabel" layoutX="24.0" layoutY="155.0" opacity="0.72" prefHeight="15.0" prefWidth="38.0" text="0" AnchorPane.bottomAnchor="190.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="77.0">
                     <font>
                        <Font size="10.0" />
                     </font></Label>
                  <Label fx:id="orgLabel" layoutX="17.0" layoutY="219.0" opacity="0.72" prefHeight="14.0" prefWidth="38.0" text="0" AnchorPane.bottomAnchor="165.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="84.0" AnchorPane.topAnchor="219.0">
                     <font>
                        <Font size="10.0" />
                     </font></Label>
                  <Label layoutX="63.0" layoutY="155.0" text="Functions" AnchorPane.bottomAnchor="190.0">
                     <font>
                        <Font size="11.0" />
                     </font>
                  </Label>
                  <Label layoutX="56.0" layoutY="219.0" text="Organizations" AnchorPane.bottomAnchor="165.0" AnchorPane.topAnchor="213.0">
                     <font>
                        <Font size="10.0" />
                     </font>
                  </Label>
                  <Button fx:id="btnReset" layoutX="21.0" layoutY="345.0" mnemonicParsing="false" onAction="#treeTableReset" onKeyPressed="#treeTableReset" prefHeight="25.0" prefWidth="81.0" text="Reset" AnchorPane.leftAnchor="22.0" AnchorPane.rightAnchor="36.0" />
               </children></AnchorPane>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" SplitPane.resizableWithParent="false">
               <children>
                  <ScrollPane fx:id="scrollPane" prefHeight="398.0" prefWidth="480.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                     <content>
                        <TreeTableView fx:id="treeTable" editable="true" prefHeight="375.0" prefWidth="246.0" />
                     </content>
                  </ScrollPane>
               </children></AnchorPane>
        </items>
      </SplitPane>
   </children>
</AnchorPane>

回答1:

FXMLLoader.getController() will return null if you call it before the load() method has been called (because if it hasn't loaded the FXML file, it doesn't know what the controller is).

But this is still not going to do what you want. You want a reference to the controller instance that is associated with the root of the FMXL file that is displayed in the UI. It doesn't help to just get an arbitrary controller instance, whether you do it by getting it from another FXMLLoader or by instantiating it directly.

The controller instance you want is the one you can get by calling loader.getController() in the showOverview() method in your Main class. If you want the controllers to communicate with each other, you need to arrange for the RootLayoutController instance to have a reference to that OverViewController instance.

It might be better not to have the controllers have direct access to each other, but to let them communicate with data in a shared data model. This question has a simple example of this approach.