Search code examples
javalistviewjavafxnullpointerexceptionupdating

Updating ListView causes NullPointerException


I am trying to update a ListView, and that is causing java.lang.NullPointerException when list.setItems(newObList) is excuted, tried changing the the ObservableList reference to a new updated list, and that did nothing. Clearing the list first, then resetting the items also causes NullPointerException. Also tried setting the items to null, then resetting them, also NullPointerException. I am simply confused, and I have no idea how to fix this. Please guide me.

The exception happens in the refreshData() method at the list.setItems(obList); statement. It happens when the close button in the secondary stage is pressed (the "insert new word" stage"), that button calls the closeInsertWord() method, which called the refreshData() method. I am using only one controller class, called MainController.java.

public class MainController implements Initializable{

@FXML
private ListView<String> list;


@FXML
TextField iWord, iMeanings, iSyn, iAnt;


public AVLTree dictTree;
public ArrayList<String> aList;
ObservableList<String> obList;

@Override
public void initialize(URL location, ResourceBundle resources)
{
    dictTree = readData();
}

public void fillList()
{

    aList = new ArrayList<>();

    aList.add("Word: meaning1, meaning2, ... , meaningN / a synonym * an antonym");
    dictTree.inOrder(aList);

    obList = FXCollections.observableArrayList(aList);
    list.setItems(obList);
}

// SOME UNIMPORTANT CODE HERE

public void openInsertWord() throws Exception
{
    Stage primaryStage2 = new Stage();

    Parent root = FXMLLoader.load(getClass().getResource("InsertUI.fxml"));
    primaryStage2.setTitle("Insert a word");
    primaryStage2.getIcons().add(new Image("file:icon.png"));
    primaryStage2.setScene(new Scene(root));
    primaryStage2.setResizable(false);
    primaryStage2.initStyle(StageStyle.UNDECORATED);
    primaryStage2.show();
}

public void insertWord()
{
    String word = iWord.getText();

    String synonym = iSyn.getText();
    String antonym = iAnt.getText();

    String[] meanings = iMeanings.getText().split("[, ]+");

    dictTree.insert(word, meanings, synonym, antonym);
}

public void refreshData()
{
    aList = new ArrayList<>();

    aList.add("Word: meaning1, meaning2, ... , meaningN / a synonym * an antonym");
    dictTree.inOrder(aList);

    obList = FXCollections.observableArrayList(aList);

    list.setItems(obList); // The exception happens here
}

public void closeInsertWord()
{
    (iWord).getScene().getWindow().hide();
    refreshData();
}

The Main Class:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;

public class Main extends Application
{

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("MainUI.fxml"));
        primaryStage.setTitle("dict");
        primaryStage.getIcons().add(new Image("file:icon.png"));
        primaryStage.setScene(new Scene(root));
        primaryStage.setResizable(false);
        primaryStage.show();
    }

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

The stack trace:

    Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
    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.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.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.Node.fireEvent(Node.java:8413)
    at javafx.scene.control.Button.fire(Button.java:185)
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
    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.EventDispatchChainImp`enter code here`l.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:381)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
    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:748)
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:498)
    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:498)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
    ... 48 more
Caused by: java.lang.NullPointerException
    at MainController.refreshData(MainController.java:155)
    at MainController.closeInsertWord(MainController.java:161)
    ... 58 more

Main stage FXML file's content (MainUI.fxml):

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.effect.InnerShadow?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="698.0" prefWidth="582.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
   <children>
      <Label layoutX="257.0" layoutY="42.0" text="Dictionary" textFill="#720000">
         <font>
            <Font name="System Bold" size="14.0" />
         </font>
      </Label>
      <ListView fx:id="list" layoutX="22.0" layoutY="72.0" prefHeight="148.0" prefWidth="541.0">
         <effect>
            <InnerShadow blurType="TWO_PASS_BOX" />
         </effect></ListView>
      <MenuBar prefHeight="20.0" prefWidth="582.0">
        <menus>
          <Menu mnemonicParsing="false" text="File">
            <items>
              <MenuItem fx:id="load" mnemonicParsing="false" onAction="#fillList" text="Load Data" />
                  <MenuItem fx:id="export" mnemonicParsing="false" onAction="#export" text="Export to a file" />
                  <MenuItem fx:id="close" mnemonicParsing="false" onAction="#close" text="Close" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Available Operations">
            <items>
              <MenuItem fx:id="InsertNew" mnemonicParsing="false" onAction="#openInsertWord" text="Insert new word" />
                  <MenuItem fx:id="showPost" mnemonicParsing="false" text="Show Postfix Expressions" />
                  <MenuItem fx:id="evPost" mnemonicParsing="false" text="Evaluate Postfix Expressions" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </children>
</AnchorPane>

The secondary stage's FXML file (InsertUI.fxml):

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>


<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="297.0" prefWidth="503.0" style="-fx-border-color: #720000; -fx-border-width: 3;" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
   <children>
      <Label layoutX="176.0" layoutY="23.0" text="Insert a new word" textFill="#720000">
         <font>
            <Font name="System Bold" size="18.0" />
         </font>
      </Label>
      <Label layoutX="32.0" layoutY="74.0" text="Word">
         <font>
            <Font name="System Bold" size="13.0" />
         </font>
      </Label>
      <Label layoutX="32.0" layoutY="116.0" text="Meaning(s)">
         <font>
            <Font name="System Bold" size="13.0" />
         </font>
      </Label>
      <Label layoutX="32.0" layoutY="159.0" text="Synonym">
         <font>
            <Font name="System Bold" size="13.0" />
         </font>
      </Label>
      <Label layoutX="32.0" layoutY="204.0" text="Antonym">
         <font>
            <Font name="System Bold" size="13.0" />
         </font>
      </Label>
      <TextField fx:id="iWord" layoutX="134.0" layoutY="71.0" />
      <TextField fx:id="iMeanings" layoutX="134.0" layoutY="113.0" prefHeight="25.0" prefWidth="355.0" />
      <TextField fx:id="iSyn" layoutX="134.0" layoutY="156.0" />
      <TextField fx:id="iAnt" layoutX="134.0" layoutY="201.0" />
      <Button layoutX="355.0" layoutY="245.0" mnemonicParsing="false" onAction="#insertWord" text="Insert" textFill="#720000">
         <font>
            <Font name="System Bold" size="16.0" />
         </font>
      </Button>
      <Button layoutX="444.0" layoutY="263.0" mnemonicParsing="false" onAction="#closeInsertWord" text="close" />
   </children>
</AnchorPane>

The GUI screenshot here


Solution

  • closeInsertWord() is called on the controller for the second FXML file, as the handler for a button defined in that file. There is no element in that FXML file with fx:id="list", so list is null in that controller. So you get a null pointer exception when you call list.setItems().

    It is generally a bad idea to use the same class for controllers associated with two different FXML files, as it gets very difficult to keep track of which fields are initialized and which are null in each of the different controller instances.

    Create a different controller class for each FXML file. Pass the data to the second controller that it needs when you load that file.

    Something like:

    public class MainController implements Initializable{
    
        @FXML
        private ListView<String> list;
    
        private AVLTree dictTree;
    
        @Override
        public void initialize(URL location, ResourceBundle resources)
        {
            dictTree = readData();
        }
    
        public void fillList()
        {
    
            List<String> aList = new ArrayList<>();
            String data = "Word: meaning1, meaning2, ... , meaningN / a synonym * an antonym";
            aList.add(data);
            dictTree.inOrder(aList);
    
            list.getItems.add(data);
        }
    
        public void openInsertWord() throws Exception
        {
            Stage primaryStage2 = new Stage();
    
            FXMLLoader loader = new FXMLLoader(getClass().getResource("InsertUI.fxml"));
            Parent root = FXMLLoader.load();
    
            InsertWordController controller = loader.getController();
            // seems like these two things really contain the same information...
            // I don't think you need both of them?
            controller.setDictTree(dictTree);
            controller.setItemsList(list.getItems());
    
            primaryStage2.setTitle("Insert a word");
            primaryStage2.getIcons().add(new Image("file:icon.png"));
            primaryStage2.setScene(new Scene(root));
            primaryStage2.setResizable(false);
            primaryStage2.initStyle(StageStyle.UNDECORATED);
            primaryStage2.show();
        }
    }
    

    And then the controller for the second FXML file:

    public class InsertWordController {
    
        @FXML
        private TextField iWord;
        @FXML
        private TextField iMeanings ;
        @FXML
        private TextField iSyn ;
        @FXML
        private TextField iAnt ;
    
        private AVLTree dictTree ;
        private List<String> itemsList ;
    
        public void setDictTree(AVLTree dictTree) {
            this.dictTree = dictTree ;
        }
    
        public void setItemsList(List<String> itemsList) {
            this.itemsList = itemsList ;
        }
    
        public void closeInsertWord()
        {
            iWord.getScene().getWindow().hide();
            String data = "Word: meaning1, meaning2, ... , meaningN / a synonym * an antonym" ;
            List<String> aList = new ArrayList<>();
            aList.add(data);
            dictTree.inOrder(aList);
            itemsList.add(data);
        }
    }
    

    And of course change the fx:controller attribute in the second FXML file.