Issue
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;
}
}
Solution
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.");
}
}
};
}
Answered By - fabian
Answer Checked By - Clifford M. (JavaFixing Volunteer)