Issue
I'm trying to let a program react when a line is added in a TableView, but the ItemProperty doesn't notify the listeners when the list is changed.
I think that might be because I don't 'set' the items (as a new different list) but just add to the list, but I'm not sure.
I found this on stackoverflow, but I don't think it's the same problem (or at least the solution didn't work for me).
Is there any convenient way to fix this?
minimal reproducible example:
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Random;
public class Main extends Application {
private static Random RG = new Random();
@Override
public void start(Stage stage) throws Exception {
VBox vBox = new VBox();
TableView<Integer> integerTableView = new TableView<>();
TableColumn<Integer, Integer> testColumn = new TableColumn<>("test");
testColumn.setCellValueFactory(i -> new SimpleIntegerProperty(i.getValue().intValue()).asObject());
integerTableView.getColumns().add(testColumn);
Button button = new Button("Add random number");
button.setOnAction(e -> {
integerTableView.getItems().add(RG.nextInt());
});
vBox.getChildren().addAll(integerTableView, button);
Scene scene = new Scene(vBox);
stage.setScene(scene);
stage.show();
integerTableView.itemsProperty().addListener(e -> System.out.println("Added item"));
}
public static void main(String[] args) {
launch(args);
}
}
Solution
Problem & Solution
You're adding the InvalidationListener
to the items
property, which is an instance of ObjectProperty
. A listener on that property will not know when something happens to the value it contains (it doesn't even know if/when its value is observable). You need to add the listener to the ObservableList
itself.
// javafx.beans.Observable
integerTableView
.getItems()
.addListener((Observable e) -> System.out.println("Items invalidated!"));
ListChangeListener
Note that an InvalidationListener
on an ObservableList
will be fired for any kind of change done to the list, and you won't be aware of what kind of change was done. If you want details about the change, then you should use a ListChangeListener
.
// javafx.collections.ListChangeListener
integerTableView.getItems().addListener((ListChangeListener<Integer>) c -> {
while (c.next()) {
// process change (see documentation for more info)
}
});
For both types of listeners, if you replace the list in the items
property (e.g., integerTableView.setItems(newList)
), then you'll need to add the listener to the new list in order to keep being notified of changes. This is where adding a listener to the items
property itself would be useful; here you'd probably want to use a ChangeListener
to make it easier to remove the listener from the old list and add it to the new list.
Answered By - Slaw
Answer Checked By - Gilberto Lyons (JavaFixing Admin)