Listeners of ItemProperty() of TableView don't get invalidated (javafx)?

Viewed 114

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);
    }
}
2 Answers

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)
  }
});

replace

integerTableView.itemsProperty().addListener(e -> System.out.println("Added item"));}

by this

integerTableView.getItems().addListener((ListChangeListener )(e -> {System.out.println("Added item");}));

!!!! you need to cast lambda (ListChangeListener ) otherwise you will get "reference to addlistener is ambiguous " warning , because there are more listeners for that Class

Related