2

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

BaraSec
  • 23
  • 1
  • 7
  • Post the complete stack trace, and the FXML. Where does the exception occur? During the call to `fillList()`? Or during `refreshData()`? If so, where is that method being called from? – James_D Nov 28 '17 at 19:44
  • @James_D All updated. Added everything. – BaraSec Nov 28 '17 at 19:57
  • 1
    `closeInsertWord()` is called on the controller for the second FXML 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()`. Start by creating different classes for the different controllers: it is way too confusing (as you have discovered) when you use the same class for controllers for different FXML files. You can pass the backing list for the `ListView` to the second controller (so it can add stuff to it) using https://stackoverflow.com/questions/14187963/ – James_D Nov 28 '17 at 20:05
  • I tried multiple controllers/nested controllers, but got confused and failed, so I reverted back to 1 controller. I have to learn them then. Do you recommend me a specific resource ? I will try passing the backing list (which I didn't know such thing exists), and let you know of the results. Thanks! – BaraSec Nov 28 '17 at 20:12
  • "Reverted back to 1 controller". You mean "one controller *class*", of course. You still have two controllers. By "backing list" I just meant the list you get from `list.getItems()`, i.e. the list of data that the `ListView` is displaying. – James_D Nov 28 '17 at 20:13
  • Yes sorry, one controller class is what I mean. I checked that resource and I am failing at translating that into my situation. I am very beginner at UI and Java in general. Can I show you a screenshot of a few code lines how I tried to apply multiple controllers, and explain to me how is it wrong ? – BaraSec Nov 28 '17 at 20:22
  • I added an answer. If you can't make that work, I recommend you create a very simple application that has only the features you are struggling to implement (a `ListView` and a button in one window; have the button open a new FXML file with simply a `TextField` and button, and when that button is pressed add the text in the text field to the `ListView`). If you can't make that work, post the entire thing (should be ~100 lines of code total) as a new question. – James_D Nov 28 '17 at 20:58
  • Sorry to take a long time. It worked! Thanks a lot! – BaraSec Nov 28 '17 at 21:36

1 Answers1

0

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.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • With a few modification to adapt with what I have, it worked flawlessly! Thanks a lot for giving me so much of your time :) – BaraSec Nov 28 '17 at 21:37