JavaFX custom list cell, updateItem, called a lot

I am using ListView

JavaFX in my application. The items in the list require more than just strings to display them, so I made a custom implementation ListCell<T>

where T

is the class of objects I'm displaying. In this custom class, I create BorderPane

in the constructor and override updateItem(T item, boolean empty)

. The idea was that when called, updateItem

I set the cell's graphics as BorderPane, changing some properties Label

inside it. However, I noticed that updateItem

it is not called once (for example when the cell is replayed by scrolling or a new item is added), but it is called all the time, eg. when focus changes from one cell to another (even without scrolling) or when the scene is resized or when a button is pressed inside the border area, ...

I need to have ListView

with custom ListCell

s. I want to get a callback one when a cell is reused by passing in a new item to be displayed as a parameter. Then I want to use this element as a model to create a view-view pair from which I take a view and use it as graphic

cells. Buttons and other controls inside this view should work. Is this possible in JavaFX?

Related problem:

+3


source to share


2 answers


The basic idea is that cells are rarely created, but the method updateItem(...)

is likely to be called frequently. There is no actual guarantee that the item has actually changed between calls updateItem(...)

. The default implementation updateItem(...)

will take care of handling selection, focus, etc., so this method probably needs to be called if any of these properties change (even if the element hasn't changed).

Therefore, you should strive to reduce the method overhead updateItem(...)

. It is not clear that multiple frequent calls would prevent you from doing what you want (for example, when you pass a new element as a parameter to your model, check if it is really different from the one you already have before doing any updates).

I would say that it is updateItem(...)

indeed somewhat unnamed: it is called not only when updating item

, but indeed when a cell might need to be updated. There's already a mechanism for executing the code only when it changes item

: just register a listener with a cell itemProperty()

. You can use this method (which I usually prefer) to create a different style 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 might work better in your scenario.

+8


source


I'm just getting started with JavaFX and I won't be able to explain all the small details for why it worked (it calls a method on new and existing elements), but the way I solved the problem by simply calling myContainer.getChildren().removeAll( ... )

In my ListCell implementation, public class MyCell extends ListCell<MyContent>

I create many different items and add them to a container that becomes a line in my list view. So I just had to remove the same items from this container right before adding them back. Here's a simplified version of what I did. Everything seems to be working fine. I tried James_D's suggestion, but it didn't work for me and produced the same errors.



@Override
    public void updateItem(MyContent item, boolean empty) {

        super.updateItem(item, empty);

        if (item == null || empty == true) {
            setGraphic(null);
            setText(null);
        } else {    

            // here I user the "item" to decide how to create 
            // objects a, b, c ... which are Rectangle, Label and Label
            // I also update myMainContainer, which is a Pane

            myMainContainer.getChildren().removeAll(a, b, c);
            myMainContainer.getChildren().addAll(a, b, c);
            setGraphic(myMainContainer);
        }
    }

      

0


source







All Articles