You have a bug in there: you need to make sure you remove listeners from old items when the item is updated. Remember that ListCells are reused, so updateItem(...) is called multiple times during the lifespan of your ListView.
I don't know if that's what is causing it to fail to update. This works for me:
import java.util.Random;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewUpdatableProperties extends Application {
@Override
public void start(Stage primaryStage) {
final ListView<Item> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final Random rng = new Random();
for (int i=1; i<=20; i++) {
listView.getItems().add(new Item("Item "+i, rng.nextInt(100)));
}
BorderPane root = new BorderPane();
root.setCenter(listView);
listView.setCellFactory(new Callback<ListView<Item>, ListCell<Item>>() {
@Override
public ListCell<Item> call(ListView<Item> param) {
return new ItemListCell();
}
});
HBox controls = new HBox();
controls.setPadding(new Insets(5));
Button incButton = new Button("Increment selected");
incButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
for (Item item : listView.getSelectionModel().getSelectedItems()) {
item.increment();
}
}
});
controls.getChildren().add(incButton);
root.setBottom(controls);
Scene scene = new Scene(root, 250, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class ItemListCell extends ListCell<Item> implements InvalidationListener {
private final HBox hbox ;
private final Label nameLabel ;
private final Label valueLabel ;
public ItemListCell() {
hbox = new HBox(5);
nameLabel = new Label();
valueLabel = new Label();
hbox.getChildren().addAll(nameLabel, valueLabel);
setGraphic(hbox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
@Override
public void updateItem(Item item, boolean empty) {
Item oldItem = getItem();
if (oldItem != null) {
oldItem.valueProperty().removeListener(this);
}
super.updateItem(item, empty);
if (item != null) {
nameLabel.setText(item.getName());
valueLabel.setText(String.valueOf(item.getValue()));
item.valueProperty().addListener(this);
} else {
nameLabel.setText("");
valueLabel.setText("");
}
}
@Override
public void invalidated(Observable observable) {
final int value = getItem().getValue();
System.out.println("Invalidated: item is "+getItem().getName() + " with value "+value);
valueLabel.setText(String.valueOf(value));
}
}
public static class Item {
public Item(String name, int value) {
setName(name);
setValue(value);
}
private final StringProperty name = new SimpleStringProperty(this, "name");
public StringProperty nameProperty() {
return name ;
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
public IntegerProperty valueProperty() {
return value ;
}
public int getValue() {
return value.get();
}
public void setValue(int value) {
this.value.set(value);
}
public void increment() {
value.set(value.get()+1);
}
}
public static void main(String[] args) {
launch(args);
}
}
As stated in the other answer, there is no repaint() method in JavaFX. If you wire things up correctly, when the properties are invalidated, it will know to repaint.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…