Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX custom list cell, updateItem being called a lot

I'm using a ListView in a JavaFX application. The items in the list require more than just a string to display them so I made a custom implementation of ListCell<T>, where T is the class of the objects I'm displaying. In this custom class, I create a BorderPane in the constructor and override updateItem(T item, boolean empty). The idea was that when updateItem is called, I set the graphic of the cell to be the BorderPane while change some properties of Labels inside of it. However, I noticed updateItem is not called once (e.g. when a cell is recycled through scrolling or a new item is added), but it is called all the time, e.g. when the focus changes from one cell to another (even without scrolling), or when the scene is resized, or when a button inside the borderpane is pressed, ...

I need to have a ListView with custom ListCells. I want to receive one callback when a cell is reused, passing the new item to be rendered as a parameter. Then I want to use that item as a model to construct a view-controller pair of which I take the view and use it as the graphic of the cell. Buttons and other controls inside this view should work. Is this possible in JavaFX?

Linked problem:

  • Weird recycle of cells in ListView - JavaFX
  • javafx listview with button in each cell (I've done this btw and the button callback works, but there are still way too many callbacks to updateItem)
like image 978
RDM Avatar asked Aug 11 '14 15:08

RDM


1 Answers

The basic idea is that cells are constructed rarely but the updateItem(...) method is likely to be called frequently. There is no actual guarantee that the item has really changed between calls to updateItem(...). The default implementation of updateItem(...) takes care of handling selection, focus, etc, so this method probably needs to be invoked if any of those properties change (even if the item has not changed).

You should therefore strive to reduce the overhead of the updateItem(...) method. It's not clear that multiple, frequent calls would prevent you doing what you want (for example, when you pass the new item as a parameter to your model, check to see if it's really different to the one you already have before doing any updates).

I'd argue that updateItem(...) is really somewhat misnamed: it's called not only when the item is updated, but really any time the cell might need to be updated. There's already a mechanism for executing code only when the item changes though: just register a listener with the cell's itemProperty(). You can use this technique (which I generally prefer) to create a different style of ListCell:

ListView<...> listView = ... ;
listView.setCellFactory(lv -> {
    BorderPane cellRoot = new BorderPane();
    // create nodes, register listeners on them, populate cellRoot, etc...
    ListCell<...> cell = new ListCell<>();
    cell.itemProperty().addListener((obs, oldItem, newItem) -> {
        if (newItem != null) {
            // update cellRoot (or its child nodes' properties) accordingly
        }
    });
    cell.emptyProperty().addListener((obs, wasEmpty, isEmpty) -> {
        if (isEmpty) {
            cell.setGraphic(null);
        } else {
            cell.setGraphic(cellRoot);
        }
    });
    cell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
    return cell ;
});

This approach may work better in your scenario.

like image 168
James_D Avatar answered Sep 28 '22 00:09

James_D