0
votes

I am trying to create custom cells in a ListView , but every time I add a new item, the updateItem(TextFlow item, Boolean empty) is executed twice: one time it receives null and true, and the second time it does not (!null and false)

If I do not implement the setCellFactory method, then I can add the items to the table without problems.

ListView without custom cellFactory

However, when I do implement it, it simply creates 10 empty cells (where is the content?).

ListView with custom cellFactory

public class Controller implements Initializable {

@FXML
private ListView <TextFlow> console;

private ObservableList<TextFlow> data = FXCollections.observableArrayList();

public void initialize(URL location, ResourceBundle resources) {

    console.setCellFactory(new Callback<ListView<TextFlow>, ListCell<TextFlow>>() {

        @Override
        public ListCell<TextFlow> call(ListView<TextFlow> param) {
            return new ListCell<TextFlow>() {
                @Override
                protected void updateItem(TextFlow item, boolean empty) {
                    super.updateItem(item, empty);

                    if (item != null) {
                        setItem(item);
                        setStyle("-fx-control-inner-background: blue;");
                    } else {
                        System.out.println("Item is null.");
                    }

                }
            };
        }

    });


    for (int i = 0 ; i < 10; i++) {
        Text txt = getStyledText("This is item number " + i + ".");
        TextFlow textFlow = new TextFlow();
        textFlow.getChildren().add(txt);
        data.add(textFlow);
    }

    console.setItems(data);

}

private Text getStyledText (String inputText) {
    Text text = new Text(inputText);
    text.setFont(new Font("Courier New",12));
    text.setFill(Paint.valueOf("#000000"));
    return text;
}
}
1

1 Answers

2
votes

updateItem can be called an arbitrary amount of times, different items may be passed and the cell can go from empty to non-empty and the other way round. ListView creates about as many cells as you see on screen and fills them with items. E.g. scrolling or modifications of the items list or resizing of the ListView can result in updates.

For this reason any cell needs to be able to deal with an arbitrary sequence of items (or null+empty) being passed to the updateItem method.

Furthermore you should avoid invoking setItem yourself, since super.updateItem does that already. Use setGraphic instead, if you want to display the item in the cell:

@Override
public ListCell<TextFlow> call(ListView<TextFlow> param) {
    return new ListCell<TextFlow>() {
        @Override
        protected void updateItem(TextFlow item, boolean empty) {
            super.updateItem(item, empty);

            if (item != null) {
                setStyle("-fx-control-inner-background: blue;");
                setGraphic(item);
            } else {
                setStyle(null);
                setGraphic(null);
                System.out.println("Item is null.");
            }

        }
    };
}