从ObjectProperty更新事件(就像在ObservableList中一样)

时间:2017-07-13 13:02:05

标签: java javafx propertychangelistener

如果其中一个元素更改了其中一个属性(更新事件),我可以使用提取器(Callback<E, Observable[]> extractor)发出ListProperty个火灾更改事件。

Update Change Event in ObservableList

是否有ObjectProperty<>的等价物?我有一个SimpleObjectProperty,当它的值(另一个bean类型)的属性发生变化时,我想触发事件(更新变更事件)。

示例代码:

public class TestBean {

    public static <T extends TestBean> Callback<T, Observable[]> extractor() {

    return (final T o) -> new Observable[] { o.testPropertyProperty() };
    }

    private final StringProperty testProperty = new SimpleStringProperty();

    public final StringProperty testPropertyProperty() {
    return this.testProperty;
    }

    public final String getTestProperty() {
    return this.testPropertyProperty().get();
    }

    public final void setTestProperty(final String testProperty) {
    this.testPropertyProperty().set(testProperty);
    }

}

public class SomeType {

    /**
     * How can I listen for changes of TestBean#testProperty?
     */
    private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();

}

如果SomeType#property的值发生变化,我希望收到更改事件,如果SomeType#property#testProperty发生变化,我也希望收到更改事件。

我不能只听SomeType#property#testProperty,因为当SomeType#property被更改时我不会收到通知(然后我会听错对象的更改)。

4 个答案:

答案 0 :(得分:1)

  

如果SomeType#property的值发生变化,我希望收到更改事件,如果SomeType#property#testProperty发生变化,我也希望收到更改事件。

     

我不能只听SomeType#property#testProperty,因为当SomeType#property被更改时我不会收到通知(然后我会听错对象的更改)。

这是对JavaFX当前迭代的各种限制。内置方式不可靠,您最好使用第三方库。有关详细信息,请参阅此answer

对于您的情况,ReactFX可以以类似的方式使用:

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

import org.reactfx.value.Val;
import org.reactfx.value.Var;

class TestBean {

    private final StringProperty testProperty = new SimpleStringProperty();
    public final StringProperty testPropertyProperty()        { return testProperty; }
    public final String getTestProperty()                     { return testProperty.get(); }
    public final void setTestProperty(String newTestProperty) { testProperty.set(newTestProperty); }
}

public class SomeType {

    private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
    public final ObjectProperty<TestBean> propertyProperty() { return property; }
    public final TestBean getProperty()                      { return property.get(); }
    public final void setProperty(TestBean newProperty)      { property.set(newProperty); }

    public static void main(String[] args) {
        SomeType someType = new SomeType();
        Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
        chainedTestProperty.addListener((obs, oldVal, newVal) -> System.out.println(obs + " " + oldVal + "->" + newVal));

        //Tests
        someType.setProperty(new TestBean());
        someType.getProperty().setTestProperty("s1");
        TestBean bean2 = new TestBean();
        bean2.setTestProperty("s2");
        someType.setProperty(bean2);
        someType.setProperty(new TestBean());
    }
}

输出:

org.reactfx.value.FlatMappedVar@7aec35a null->s1 
org.reactfx.value.FlatMappedVar@7aec35a s1->s2 
org.reactfx.value.FlatMappedVar@7aec35a s2->null

关键线

Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);

是一种听众链接。第一个参数是某种类型OvservableValue的属性(Type)。第二个参数是Type2中某些其他类型Type的&#34; sub&#34; -property,它是从Type到该属性的函数。

现在每当有任何&#34;链接&#34;在链变化中,您会收到通知。你可以通过这种方式连续链接ovservable来继续监听sub-sub -...属性的变化。

答案 1 :(得分:1)

上周我遇到了同样的问题,经过多次尝试,我找到了一个似乎按预期工作的解决方案:

  • 我创建了一个名为ObjectXProperty<E>的新类,它具有ObjectProperty<E>的相同接口;
  • 它的构造函数可以接受Callback<E,Observable[]>,我们的提取函数;
  • ObjectXProperty内,我使用SimpleObjectProperty删除所有方法;
  • 神奇的诀窍在于set(E value)方法:我创建一个ObjectBinding只需发回,但它使用提取函数来决定何时它变得无效!

  • 如果先前在bind上使用ObjectXProperty方法,则不会应用此技巧,让“真正的”绑定完成他的工作;如果调用unbind方法,它将再次起作用;

这是我的新课程ObjectXProperty<E>

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.util.Callback;

/**
 *
 * @author Claude Bouchard - 2017
 */

public class ObjectXProperty<E> extends ObjectProperty<E> {

SimpleObjectProperty<E> p;
Callback<E, Observable[]> extractor;

boolean externalBound = false;

public ObjectXProperty(Callback<E, Observable[]> extractor) {
    this.extractor = extractor;
}

public ObjectXProperty(E init, Callback<E, Observable[]> extractor) {

    p = new SimpleObjectProperty();
    this.extractor = extractor;
    set(init);

}

public ObjectXProperty(Object bean, String name, Callback<E, Observable[]> extractor) {
    p = new SimpleObjectProperty(bean, name);
    this.extractor = extractor;
}

public ObjectXProperty(Object bean, String name, E init, Callback<E, Observable[]> extractor) {
    p = new SimpleObjectProperty(bean, name);
    this.extractor = extractor;
    set(init);

}

@Override
public void set(E value) {
    if (!externalBound) {
        if (value != null) {
            p.bind(Bindings.createObjectBinding(() -> {
                return value;
            }, extractor.call(value)));

        } else {
            p.bind(Bindings.createObjectBinding(() -> {
                return value;
            }, new Observable[]{}));
        }
    } else {
        p.set(value); //As expected, it will throw a java.lang.RuntimeException
    }
}

@Override
public E get() {
    return p.get();
}

@Override
public void addListener(ChangeListener<? super E> listener) {
    p.addListener(listener);
}

@Override
public void removeListener(ChangeListener<? super E> listener) {
    p.removeListener(listener);
}

@Override
public void addListener(InvalidationListener listener) {
    p.addListener(listener);
}

@Override
public void removeListener(InvalidationListener listener) {
    p.removeListener(listener);
}

@Override
public Object getBean() {
    return p.getBean();
}

@Override
public String getName() {
    return p.getName();
}

@Override
public void bind(ObservableValue<? extends E> observable) {
    p.bind(observable);
    externalBound = true;
}

@Override
public void unbind() {
    p.unbind();
    externalBound = false;
    set(get()); //to reactivate the extractor on the last value
}

@Override
public boolean isBound() {
    return externalBound;
}

}

答案 2 :(得分:0)

我想出了以下内容:

ExpressionHelper$SingleChange

不幸的是,由于@Override protected void fireValueChangedEvent() { final T oldValue = currentValue; currentValue = observable.getValue(); final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue); if (changed) { try { listener.changed(observable, oldValue, currentValue); } catch (Exception e) { Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } } }

,这不会触发任何更改事件
fireValueChangedEvent()

这会检查是否相等,只有在不相等的情况下才会通知所有侦听器。当我触发angular.element(appElement).scope()时,值已经改变,并且新旧值相等,因此没有通知听众。

答案 3 :(得分:-1)

我认为你需要为你的对象添加一个监听器。这可以简单地完成。首先,你应该用构造函数和getter这样编写你的类:

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

public class SomeType {

    public ObjectProperty<TestProperty> property;

    public SomeType(TestProperty testProperty) {
        this.property = new SimpleObjectProperty<>(testProperty);
    }

    public TestProperty getProperty() {
        return property.get();
    }

    public ObjectProperty<TestProperty> propertyProperty() {
        return property;
    }
}

然后,只要您拥有SomeType的实例,就可以链接属性,这样就可以获得property属性的testProperty(),然后只需添加一个监听器。

someType.getProperty().testProperty().addListener((observable, oldValue, newValue) -> {
        // Do whatever you want if the its value changed.
        // You can also use its old or new value.
    });