Getting Null Pointer exception and Can't understand why

My program seems to work fine when scrolling up and down in the main ListView. The problem happens when I open a TitledPane and scroll down or up fast using the mouse scroll-wheel. I also noticed that if I use the mouse to drag the scrollbar after opening a TitledPane, everything works fine. I would like to think that I am very good a spotting and fixing my NullPointer errors, but this one has me baffled. How do I pinpoint the cause of the error and how do I fix it. I can probably figure the second part out if I could understand what is going on.

Main

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class NPEDiggingSO extends Application
{

    private static class OuterListCell extends ListCell<MainListViewCellData>
    {
        private final ListView<Note> cellListView;

        public OuterListCell()
        {
            setPrefHeight(300);
            setPrefWidth(300);

            cellListView = new ListView<>();
            cellListView.setCellFactory(v -> new NoteCell());
        }

        @Override
        protected void updateItem(MainListViewCellData item, boolean empty)
        {
            super.updateItem(item, empty);
            if (item == null || empty) {
                setText(null);
                setGraphic(null);
            }
            else {
                cellListView.getItems().setAll(item.getNotes());
                setGraphic(cellListView);
            }
        }

    }

    private Parent createContent()
    {
        DataModel model = new DataModel();
        ListView<MainListViewCellData> outer = new ListView<>(model.getMainListViewData());
        outer.setCellFactory(c -> new OuterListCell());
        BorderPane content = new BorderPane(outer);
        return content;
    }

    @Override
    public void start(Stage stage) throws Exception
    {
        stage.setScene(new Scene(createContent(), 700, 500));
        //stage.setTitle(FXUtils.version());
        stage.show();
    }

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

}

DataModel

import java.util.ArrayList;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

/**
 *
 * @author blj0011
 */
public class DataModel
{
    public DataModel()
    {
    }

    public ObservableList<MainListViewCellData> getMainListViewData()
    {
        ObservableList<MainListViewCellData> observableList = FXCollections.observableArrayList();

        for (int i = 0; i < 250; i++) {
            MainListViewCellData mainListViewCellData = new MainListViewCellData();
            List<Note> notes = new ArrayList();
            notes.add(new Note("note title " + i, "note text " + 1));
            mainListViewCellData.setNotes(notes);
            observableList.add(mainListViewCellData);
        }

        return observableList;
    }
}

MainListViewCellData

    import java.util.List;
    import javafx.collections.FXCollections;

    /**
     *
     * @author blj0011
     */
    public class MainListViewCellData
    {
        private List<Note> notes;

        public MainListViewCellData(List<Note> notes)
        {
            this.notes = notes;
        }

        public MainListViewCellData()
        {
            this.notes = FXCollections.observableArrayList();
        }

        public List<Note> getNotes()
        {
            return notes;
        }

        public void setNotes(List<Note> notes)
        {
            this.notes = notes;
        }

        @Override
        public String toString()
        {
            return '{' + "notes=" + notes + '}';
        }
    }

Note

public class Note
{
    private String title;
    private String text;

    public Note(String title, String text)
    {
        this.title = title;
        this.text = text;
    }

    public String getText()
    {
        return text;
    }

    public void setText(String text)
    {
        this.text = text;
    }

    public String getTitle()
    {
        return title;
    }

    public void setTitle(String title)
    {
        this.title = title;
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(", title=").append(title);
        sb.append(", text=").append(text);
        sb.append('}');
        return sb.toString();
    }
}

NoteCell

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextArea;
import javafx.scene.control.TitledPane;

public class NoteCell extends ListCell<Note>
{
    TextArea textArea = new TextArea();
    TitledPane titledPane = new TitledPane("", textArea);
    ObservableList<Note> observableList = FXCollections.observableArrayList();

    @Override
    public void updateItem(Note item, boolean empty)
    {
        super.updateItem(item, empty);
        if (item == null || empty) {
            setText(null);
            setGraphic(null);
        }
        else {
            titledPane.setExpanded(false);
            titledPane.setText(item.getTitle());
            titledPane.setAnimated(false);
            textArea.setText(item.getText());
            setGraphic(titledPane);
        }
    }
}

Error Message

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at com.sun.javafx.scene.control.skin.VirtualFlow$4.findOwnerCell(VirtualFlow.java:848)
    at com.sun.javafx.scene.control.skin.VirtualFlow$4.select(VirtualFlow.java:822)
    at com.sun.javafx.scene.traversal.TraversalEngine.select(TraversalEngine.java:103)
    at com.sun.javafx.scene.traversal.TopMostTraversalEngine.trav(TopMostTraversalEngine.java:77)
    at javafx.scene.Scene.traverse(Scene.java:2005)
    at javafx.scene.Scene.focusIneligible(Scene.java:2024)
    at javafx.scene.Scene.access$3400(Scene.java:159)
    at javafx.scene.Scene$ScenePulseListener.focusCleanup(Scene.java:2370)
    at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2385)
    at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:398)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:397)
    at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:424)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:561)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:541)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:534)
    at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:340)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$3(WinApplication.java:177)
    at java.lang.Thread.run(Thread.java:748)

PS

I would like to apologize for being lazy and thank @kleopatra for creating the MCVE before I got the chance too.

1 answer

  • answered 2020-02-13 14:16 kleopatra

    No solution, just a MCVE (not really yet, re-used your Note/DataModel which are not needed, a simple list of text would do) to play with (will delete once you'll have seen it) and some observations (my context is Win10, fx11):

    • happens only when scrolling with mouse wheel
    • happens only when scrolling down
    • happens only if a TitledPane near the top has been expanded once (doesn't matter whether it has been collapsed immediately after expanding)

    So, yeah, I would say it's a bug - but unable to nail it.

    The example:

    public class NPEDiggingSO extends Application {
    
        private static class OuterListCell extends ListCell<MainListViewCellData> {
            private ListView<Note> cellListView;
    
            public OuterListCell() {
                setPrefHeight(300);
                setPrefWidth(300);
    
                cellListView = new ListView<>();
                cellListView.setCellFactory(v -> new NoteCell());
            }
    
            @Override
            protected void updateItem(MainListViewCellData item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null || empty) {
                    setText(null);
                    setGraphic(null);
                } else {
                    cellListView.getItems().setAll(item.getNotes());
                    setGraphic(cellListView);
                }
            }
    
        }
    
        private Parent createContent() {
            DataModel model = new DataModel();
            ListView<MainListViewCellData> outer = new ListView<>(model.getMainListViewData());
            outer.setCellFactory(c -> new OuterListCell());
            BorderPane content = new BorderPane(outer);
            return content;
        }
    
        @Override
        public void start(Stage stage) throws Exception {
            stage.setScene(new Scene(createContent()));
            stage.setTitle(FXUtils.version());
            stage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    }