This is a known bug in JavaFX 8, which is fixed in the latest ea release (1.8.0_20).
As a workaround, create the controls once and register handlers with them, then just update their state in the updateItem(...) method:
listView.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> p) {
Label usernameLabel = new Label();
usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 12));
Button callButton = new Button("Call");
HBox usernameBox = new HBox(5);
usernameBox.setAlignment(Pos.CENTER_LEFT);
usernameBox.getChildren().addAll(usernameLabel);
BorderPane borderPane = new BorderPane();
borderPane.setLeft(usernameBox);
borderPane.setRight(callButton);
VBox vbox = new VBox(3);
vbox.getChildren().addAll(borderPane);
ListCell<String> cell = new ListCell<String>() {
@Override
protected void updateItem(String t, boolean empty) {
super.updateItem(t, empty);
if (t != null) {
usernameLabel.setText(t);
setGraphic(vbox);
} else {
setGraphic(null); // you will have weird bugs without this: don't omit it
}
}
};
callButton.setOnAction(e -> System.out.println("action: "+cell.getItem()));
callButton.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> System.out.println("entered "+ cell.getItem()));
callButton.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("clicked "+ cell.getItem()));
return cell;
}
});
Note that this "workaround" is really the preferred approach anyway, and the one that was intended by the designers of the "virtualized" controls like ListView
, TableView
, etc. The point is that updateItem(...)
is potentially called very frequently by the application, whereas cells are created very rarely. By creating new controls in the updateItem(...)
method you potentially introduce performance issues. Create them once for the cell, and then just configure them in updateItem(...)
. Note also how I just registered the event handlers once, and had the handlers refer to cell.getItem()
to see which item is currently represented by the cell.
One last thing: you have a bug in your code (which I fixed). Since cells can be reused, including for the case where a cell displaying an item is reused for an empty cell, it's important that you always handle the case where the item is null (typically by setting text and/or graphic to null).