1

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.

SedJ601
  • 12,173
  • 3
  • 41
  • 59
  • 10
    This is [way too much code](/help/how-to-ask) to ask people to read through. Your responsibility as person asking on Stackoverflow is to first reduce your code to the bare [mcve]. Remember: most of the code you're showing has nothing to do with your error: remove it, and hardcode any bits that do have something to do with your error but are, during normal code execution, retrieved from other places. And also remember that the MCVE exercise is actually primarily for your own sake: by being forced to reduce your code, you almost always discover the problem, obviating the need for posting to SO – Mike 'Pomax' Kamermans Feb 12 '20 at 23:02
  • I agree with you. I will look into shorting this tomorrow. I think the issue is a bug and that creating the M in this case will not help me find it. Just a guess though. – SedJ601 Feb 12 '20 at 23:08
  • 4
    An mcve _always_ helps you find problems. – Mike 'Pomax' Kamermans Feb 12 '20 at 23:09
  • Given that this work perfectly when using the mouse to drag the scollbar, this is most likely a bug and the NullPointer duplicate will not help me. – SedJ601 Feb 13 '20 at 00:13
  • 1
    Reopened: I didn't read all the code, but a quick look at the stack trace clearly indicates the exception is being thrown in internal JavaFX library code, not by the client code. Knowing what a NPE is and understanding references is not enough to answer this question. That said, the question really does need less code. – James_D Feb 13 '20 at 00:33
  • 2
    @Sedrick if this really is a bug (and it might very well be) you should be able to reproduce the same bug with far less code than posted. Bear in mind that you'll be asked to post minimal code if you file an official bug, too: most of this code is not required to set up the fail condition. – Mike 'Pomax' Kamermans Feb 13 '20 at 00:40
  • Controls should be created on the JavaFX Application thread, but you're messing with statics, which I'm not sure if it's executed on the Application thread. For a JavaFX control to be updated, you must update it on the Application Thread. How about using Platform.runLater(() -> your code here)); However you may want to explore other JavaFX ways of doing this, rather than your own thread. – Dragana Le Mitova Feb 13 '20 at 00:49
  • I don't have any threads in this program. – SedJ601 Feb 13 '20 at 01:47
  • @Dragana Le Mitova that makes me think. What would happen if I set the TitlePane animation to disabled. I will see tomorrow. To me, that still doesn't explain why it works when dragging the mouse and doesn't work when using the mouse wheel. – SedJ601 Feb 13 '20 at 01:53
  • OMG, After reading the last code snippet, i forgot what i read it on first. Pls put only the necessary codes. – Ahamed Safnaj Feb 13 '20 at 06:15
  • I am going to create a MCVE tomorrow. With lots of focus on the M. – SedJ601 Feb 13 '20 at 06:20
  • Which version of JavaFX are you using? – Boris Feb 13 '20 at 15:47
  • @Boris I am using `Java: 1.8.0_221` @Kleopatra verified it using `fx11`. – SedJ601 Feb 13 '20 at 15:49
  • Why are you using [6-year-old](https://en.wikipedia.org/wiki/JavaFX#JavaFX_8) JavaFX? – Boris Feb 13 '20 at 16:02
  • If I create a program that uses jars that are not compatible with `JavaFX 9+`, I use `Java 8`. The two workarounds at this point are too much of a headache. I Also use `JavaFX13`. I haven't tested this issue yet using `JavaFX13`, but I will. @Kleopratra tested it using `JavaFX11`. – SedJ601 Feb 13 '20 at 16:10

1 Answers1

3

No solution, just a MCVE to play with and some observations (my context is Win10, fx11). To see the error, click on the button in the top-most cell, then scroll down by mouseWheel. The error

  • happens only when scrolling with mouse wheel
  • happens only when scrolling down
  • happens only if a Node in the inner cell (here a simple Button) is focused when scrolling

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

The example:

public class NPEDiggingSO extends Application {

    private static class InnerListCell extends ListCell<String> {
        private Button button;

        public InnerListCell() {
            button = new Button();
        }

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null || empty) {
                setText(null);
                setGraphic(null);
            } else {
                button.setText(item);
                setGraphic(button);
            }
        }

    }

    private static class OuterListCell extends ListCell<String> {
        private ListView<String> cellListView;

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

            cellListView = new ListView<>();
            cellListView.setCellFactory(c -> new InnerListCell());
        }

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

    }

    private Parent createContent() {
        ObservableList<String> model = FXCollections.observableArrayList(
                "item1", "item2", "item3", "item4", "item5");
        ListView<String> outer = new ListView<>(model);
        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);
    }

}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • 2
    go ahead, the purpose of this not-an-answer was to help you out off the pit :)) For a bug report, please strip it down further: no need for a custom dataModel, just some setup that you can show a list inside a listCell with the inner having a TitledPane ... there's some weirdness around focus and titledPane that I couldn't nail, yet – kleopatra Feb 13 '20 at 16:52
  • Thanks for the help and advice! – SedJ601 Feb 13 '20 at 17:13