【问题标题】:How to make a complex object an Observable如何使复杂对象成为 Observable
【发布时间】:2017-02-07 06:56:06
【问题描述】:

我有多个 JavaFX 窗格和画布,它们引用了一个包含所需数据的复杂对象,我希望它们在对象更改时重绘。

这将要求对象是可观察的,但我应该使用哪个类? JavaFX 似乎主要有 ObservableValue 子类,它们包装一个值并允许将其换出。我不想换掉复杂的对象,只是在发生变化时通知侦听器。我可以通过实现addListener 来做到这一点,但我确信已经有一个子类可以为我做到这一点。

class ComplexObject /* extends SomeObservableClass */ {
    public int getValue1 { complex calculations... };
    public int getValue2 { ... };

    public void setNewValue1(int newValue) { ... }
}

class ComplexRenderer extends Canvas implements InvalidationListener {
    private ComplexObject complexObject;

    public void setComplexObject(ComplexObject complexObject) {
        this.complexObject = complexObject;
        complexObject.addListener(this);
    }

    public void draw() { ... }
}

ComplexObject 应该扩展哪个类?是否有一些东西可以维护监听器列表并有类似 fireValueChangedEvent() 的东西,所以我可以让它通知所有监听器?

我在 JavaFX 中看到的一切似乎都是针对属性的,这似乎不是正确的选择。

【问题讨论】:

  • 不太清楚“不能交换”是什么意思。我不确定您的意思是交换整个 ComplexObject 还是其中的一些值。 getValue() 只在调用时才需要计算吗?还是可以在调用之前计算和存储?
  • "Cannot swap" 表示我想保持相同的ComplexObject,只需更改其中的值即可。在我看来,ObjectPropertyBase 及其朋友被设计为包装器,因此您通过set() 替换整个对象,然后将触发失效侦听器。我可以存储复杂的计算并将结果作为单独的属性公开,但最初的意图是只拥有复杂的对象并按需进行计算。
  • 这意味着我的答案应该接近你正在寻找的。您不会跟踪 ComplexObject 参考中的更改。相反,您跟踪其内部值(要装箱的基元)和对象的引用的变化。如果您需要跟踪单个属性,则需要向它们添加单个侦听器。否则,您可以公开一个BooleanProperty,您可以侦听任何属性的任何更改。当然,在内部,您需要将这些属性绑定到 BooleanProperty

标签: java javafx observable


【解决方案1】:

不太确定你所说的交换是什么意思,也不确定我是否理解正确。

class ComplexObject {
    private IntegerProperty value1 = new SimpleIntegerProperty();
    private IntegerProperty value2 = new SimpleIntegerProperty();
    private BooleanProperty internalChanged = new SimpleBooleanProperty(false);

    public ComplexObject() {
        this.internalChanged.bind(Bindings.createBooleanBinding(() ->
            this.internalChanged.set(!this.internalChanged.get()), this.value1, this.value2));
    }

    public IntegerProperty value1Property() { return this.value1; }
    public int getValue1() { return this.value1.get(); }
    public void setValue1(int value) { return this.value1.set(value); }

    public IntegerProperty value2Property() { return this.value2; }
    public int getValue2() { return this.value2.get(); }
    public void setValue2(int value) { return this.value2.set(value); }

    public void setNewValue1(int newValue) { /* What value is this??? */ }

    public BooleanProperty internalChangedProperty() { return this.internalChanged; }
}

class ComplexRenderer extends Canvas implements InvalidationListener {
    private ComplexObject complexObject;

    public void setComplexObject(ComplexObject complexObject) {
        this.complexObject = complexObject;
        complexObject.internalChangedProperty().addListener(this);
    }
    @Override public void invalidated(Observable observable) {
        // Something inside complex object changed
    }

    public void draw() { ... }
}

【讨论】:

  • 谢谢!我想以简化的形式,我将使用 bool 来提醒侦听器并使用常规整数和访问器来获取实际值。问题: 1. 这是否意味着每次发生变化时我都需要切换布尔值? IIRC 侦听器仅在数据实际更改时触发。 2. 这看起来有点像破解系统让它做我想做的事。这是处理失效侦听器的普遍常用方法吗?
  • @EboMike 好吧,从技术上讲,您可以在没有ObservableValue 库的情况下实现所有内容。在这种情况下,您需要手动编写自己的 getter/setter,并让 setter 将布尔字段更改为 true。如果您按照我的回答,绑定 (this.internalChanged.bind(Bindings.createBooleanBinding(() -> this.internalChanged.set(true), this.value1, this.value2));) 应该为您处理它。当然,您需要将所有属性添加到其中。在内部,您可以想象为 Bindings.createBooleanBinding() 订阅所有属性的 changelistener。
  • 我做到了,而且成功了! (好吧,我正在切换布尔值,而不是仅仅将其设置为 true,但同样的事情)。我仍然觉得我完全滥用了这个系统,但它确实有效,所以我不知道我应该关心多少。谢谢!
【解决方案2】:

也许你可以看看接口ObjectPropertyBase<T> 和实现Observable 的类ObjectPropertyBase<T>SimpleObjectProperty<T>

但是你必须定义你的对象何时改变和监听逻辑。

很抱歉,这只是一个工作痕迹,但我希望它可能有用。

【讨论】:

  • 谢谢!是的,我看过那些。但是,这些也实现了ObservableValue,它与我在问题中提到的一致——它们包装了一个对象,所以我的理解是你应该在包装器中替换对象本身以表明发生了一些变化.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-31
  • 1970-01-01
  • 2012-05-20
  • 1970-01-01
  • 2011-01-26
  • 2023-03-13
  • 1970-01-01
相关资源
最近更新 更多