JavaFX InvalidationListener或ChangeListener

时间:2017-07-15 10:20:47

标签: java javafx

我只对天气属性的变化感兴趣,但不是新值。

注册InvalidationListener而不是ChangeListener

是否有利

我假设,对属性的更改首先使属性失效并通知所有失效侦听器。只有在注册了更改侦听器或者有人请求此属性时,才会“验证”/重新计算该属性,并使用新值更新所有更改侦听器。

由于我对实际值不感兴趣,因此我认为仅监听失效事件(属性已更改但未重新计算,某种中间状态)是一种性能优势。

3 个答案:

答案 0 :(得分:8)

您需要为此实施ChangeListener。只有在值无效后才会执行InvalidationListener。请参阅docs

来自ObservableValue的java文档:

  

ObservableValue生成两种类型的事件:更改事件和   失效事件。更改事件表示该值具有   改变。如果当前值为,则生成失效事件   不再有效。如果这种区别变得很重要   ObservableValue支持延迟评估,因为懒惰   评估值1不知道无效值是否确实存在   改变,直到重新计算。 因此,产生变化   事件需要急切的评估,而失效事件可以   为急切和懒惰的实现而生成。

我添加了一个简单的例子

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

使用InvalidationListener时遇到的问题是如果值再次失效,则不会收到更改通知,因为它已经无效。您必须使用更改侦听器。

在属性上注册更改侦听器将禁用延迟评估,因此每次更改侦听器被触发时都会触发invalidation事件。

在我添加的样本中尝试一下。

答案 1 :(得分:2)

来自book

  

可观察对象仅应在以下情况下生成无效事件:   其内容的状态从有效变为无效。也就是说,多个   连续的失效应该只产生一个失效事件。

一个小例子来说明

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

输出

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

money属性关联的侦听器为Invalidationlistener类型,从输出中我们可以看到InvalidationListenerChangeListener之间的事件术语有所不同。

一个更详细的示例:

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

使用ChangeListener时,只要发生更改,就会触发一个事件。使用InvalidationListener时并非如此。

来自同一本书

  

属性在其状态下会生成无效事件   值首次从有效更改为无效。中的属性   JavaFx使用惰性评估。当无效的属性变为无效时   同样,不会生成无效事件。无效的属性   重新计算时变为有效,例如,通过调用其get()   或getValue()方法。

答案 2 :(得分:0)

有两个规则要记住,如果你打算使用一个InvalidationListener。

  1. 确保它是没有问题的,你的听众可以调用,即使没有任何变化已经发生。
  2. 如果您希望因发生的每一次更改而触发您的侦听器(通常这是侦听器的目的),请确保侦听器调用可观察对象的getter(在此进行注册) 。

否则切换到的ChangeListener。