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.