Why super.updateItem(item, empty) Called in listview.cellFacoty method in javafx?


Question

Code:

mpcListView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
    @Override
    public ListCell<String> call(ListView<String> param){
        return new XCell();
    }
});

public class XCell extends ListCell<String>{
    HBox hbox = new HBox();
    Label label = new Label();
    Button button = new Button("",getImage());
    String lastItem;

    public XCell(){
        super();
        hbox.setSpacing(120);
        hbox.getChildren().addAll(label,button);
        button.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent event){
                mpcListView.getItems().remove(lastItem);
            }
        });
    }

    @Override
    protected void updateItem(String item, boolean empty){
        super.updateItem(item, empty);
        if (empty){
            lastItem = null;
            setGraphic(null);
        }else{
            lastItem = item;
            label.setText(item);
            setGraphic(hbox);
        }
    }
}

Why super.updateItem(item, empty) is called?

1
1
2/19/2016 2:30:33 PM

Accepted Answer

ListCell updateItem(...) implementation is really important as it calls Cell updateItem(...) implementation which checks if it is empty or not and calls the correct method: setItem(...) or setEmpty(...).

If you don't call super.updateItem(...) then Cell.updateItem(...) is not called and setItem(...) is not called then... nothing is drawn or the value is not updated! ListCell just adds some check before using Cell updateItem implementation, so you get two choices:

  • You can call super.updateItem(...) in your custom ListCell implementation
  • You can call setItem(...) and setEmpty(...) in your updateItem implementation and "by-pass" ListCell implementation on checks and edits stuff

Note that ListCell it is not the "base" implementation used by a ListView. Refer to the class TextFieldListCell instead, it is a great example of how it actually works, get the sources from mercurial and read, it is always the best way.

For instance, TextFieldListCell uses super.updateItem(...) to call Cell.updateItem implementation to check if it is empty or not (by using setItem or setEmpty) and then uses javafx.scene.control.cell.CellUtils.updateItem(...). This method gets the item setted in the current cell and uses after that a converter on the item to show a String in a Label.

3
3/26/2014 12:21:10 PM

The default implementation of ListCell does some standard housekeeping. For example, it registers a mouse listener that updates the ListView's selection model appropriately.

The super.updateItem(...) call invokes the superclass implementation of the method. If you omit it, selection won't work, and probably a bunch of other functionality won't work either.

In your code, the lastItem field is redundant. There's already an item property defined in the ListCell class: another job the default implementation of updateItem(...) does is to update this property. So you can omit that field and just call getItem() when you need to get the item.

It's pretty easy to forget to call super.updateItem(...). For this reason I often use an alternative approach:

mpcListView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
    @Override
    public ListCell<String> call(ListView<String> param){
        return createXCell();
    }
});

public ListCell<String> createXCell() {
    final ListCell<String> cell = new ListCell<String>();
    final HBox hbox = new HBox();
    final Label label = new Label();
    final Button button = new Button("",getImage());

    hbox.setSpacing(120);
    hbox.getChildren().addAll(label,button);
    button.setOnAction(new EventHandler<ActionEvent>(){
    @Override
        public void handle(ActionEvent event){
            mpcListView.getItems().remove(cell.getItem());
        }
    });

    cell.itemProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) {
            if (newValue == null) {
                cell.setText(null);
                cell.setGraphic(null);
            } else {
                cell.setText(newValue);
                cell.setGraphic(hbox);
            }
        }
    });

    return cell ;

}

Note that I don't subclass ListCell at all here: I just use the default implementation and update the text and graphic using a listener on the itemProperty. This is more in the spirit of "Prefer aggregation over inheritance".


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon