JavaFX InvalidationListener or ChangeListener

You need to implement a ChangeListener for that. A InvalidationListener is only executed once the value becomes invalid. See the docs.

From the java docs of ObservableValue:

An ObservableValue generates two types of events: change events and invalidation events. A change event indicates that the value has changed. An invalidation event is generated, if the current value is not valid anymore. This distinction becomes important, if the ObservableValue supports lazy evaluation, because for a lazily evaluated value one does not know if an invalid value really has changed until it is recomputed. For this reason, generating change events requires eager evaluation while invalidation events can be generated for eager and lazy implementations.

I have added a simple example

public static void main(String[] args) {

    SimpleIntegerProperty one = new SimpleIntegerProperty(1);
    SimpleIntegerProperty two = new SimpleIntegerProperty(0);

    // the binding we are interested in
    NumberBinding sum = one.add(two);
    sum.addListener(observable -> System.out.println("invalidated"));

    // if you add a value change listener, the value will NOT be evaluated lazy anymore
    //sum.addListener((observable, oldValue, newValue) -> System.out.println("value changed from " + oldValue + " to " + newValue));

    // is valid, since nothing changed so far
    System.out.println("sum valid: " + sum.isValid());
    // will invalidate the sum binding
    two.set(1);
    one.set(2); // invalidation event NOT fired here!
    System.out.println("sum valid: " + sum.isValid());
    // will validate the sum binding, since it is calculated lazy when getting the value
    System.out.println("sum: " + sum.getValue());
    System.out.println("sum valid: " + sum.isValid());
}

The problem in using the InvalidationListeneris that you will not be notified for changes if the value becomes invalid again, since it is already invalid. You have to use a change listener for that.

Registering a change listener on the property will disable lazy evaluation, so the invalidation event gets fired every time the change listener gets fired.

Try it out in the sample i added.


from the book :

An observable should generate an invalidation event only when the status of its content changes from valid to invalid. That is, multiple invalidation in a row should generate only one invalidation event.

a tiny example to show that

public class stackOverflowListenerQuestion extends Application {

    public static void main( String[] args ) {
        launch();
    }


    @Override
    public void start( Stage primaryStage ) throws Exception {
        IntegerProperty money  = new SimpleIntegerProperty(1);
        money.addListener(observable -> System.out.println("we should notify the listener"));
        money.set(10);
        money.set(20);
        money.set(30);
        System.out.println(money.getValue());


        IntegerProperty moreMoney  = new SimpleIntegerProperty(1);
        moreMoney.addListener(( observable, oldValue, newValue ) -> System.out.println("we should notify the listener very quickly"));
        moreMoney.set(100);
        moreMoney.set(200);
        moreMoney.set(300);
        System.out.println(moreMoney.getValue());
        Platform.exit();
    }
}

output

we should notify the listener
30
we should notify the listener very quickly
we should notify the listener very quickly
we should notify the listener very quickly
300

The listener associated with the money property is of type Invalidationlistener, from the output we can see the differences in term of events between the InvalidationListener and the ChangeListener.

One more detailed example:

 public class InvalidationListener extends Application {

        public static void main( String[] args ) {
            launch();
        }

        @Override
        public void start( Stage primaryStage ) throws Exception {
            Person p1 = new Person();
            Person p2 = new Person();
            Person p3 = new Person();

            NumberBinding total = Bindings.add(p1.moneyProperty().add(p2.moneyProperty()), p3.moneyProperty());
            //to see the differences between InvalidationListener and ChangeListener, yous should test them separately and watch the printed result to understand.

          //  total.addListener(( observable, oldValue, newValue ) -> System.out.println("change event occurred, we should notify the listeners"));
            total.addListener(observable -> System.out.println("Invalidation occurred, we should notify the listeners but lazily"));

            p1.setMoney(100);
            System.out.println("total.isValid() = " + total.isValid());
            p2.setMoney(200);
            System.out.println("total.isValid() = " + total.isValid());
            p3.setMoney(200);
            System.out.println("total.isValid() = " + total.isValid());
            System.out.println("total = " + total.getValue());
            System.out.println("total.isValid() = " + total.isValid());
            p3.setMoney(150);
            System.out.println("total.isValid() = " + total.isValid());
            System.out.println("total = " + total.getValue());
            System.out.println("total.isValid() = " + total.isValid());
            Platform.exit();//shutdown the JavaFx Application Thread
        }

        static class Person{
            private IntegerProperty money = new SimpleIntegerProperty();

            public final int getMoney() {
                return money.get();
            }

            public final void setMoney( int money ) {
                this.money.set(money);
            }

            public IntegerProperty moneyProperty() {
                return money;
            }
        }
    }

When you use ChangeListener, an event is fired whenever a change has occured. When you use InvalidationListener it is not the case.

from the same book

A property generates an invalidation event when the status of its value changes from valid to invalid for the first time. Properties in JavaFx use lazy evaluation. When an invalid property becomes invalid again, an invalidation event is not generated. An invalid property becomes valid when it is recomputed, for example, by calling its get() or getValue() method.

Tags:

Java

Javafx