代码之家  ›  专栏  ›  技术社区  ›  kerner1000

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

  •  1
  • kerner1000  · 技术社区  · 7 年前

    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 ,因为我不会在 已更改(然后我会在错误的对象上侦听更改)。

    4 回复  |  直到 7 年前
        1
  •  1
  •   Community CDub    4 年前

    如果值 SomeType#property 改变,但如果 SomeType#property#testProperty 变化。

    我不能只听 SomeType#property#testProperty ,因为我不会被通知,当 SomeType#属性

    这是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 在…内 类型 到那个地方。

    现在,无论何时链中的任何“链接”发生变化,都会通知您。通过这种方式连续链接ovservables,您可以继续侦听sub-sub属性中的更改。

        2
  •  1
  •   kerner1000    7 年前

    public class ObservableValueProperty<T> extends SimpleObjectProperty<T> {
    
        private InvalidationListener listener = null;
    
        private final Callback<T, Observable[]> extractor;
    
        public ObservableValueProperty() {
        this(null);
        }
    
        public ObservableValueProperty(final Callback<T, Observable[]> extractor) {
        this.extractor = extractor;
        }
    
        @Override
        protected void fireValueChangedEvent() {
        super.fireValueChangedEvent();
        }
    
        @Override
        public void setValue(final T v) {
        if (extractor != null) {
            final T oldValue = super.get();
            if (oldValue != null) {
            for (final Observable o : extractor.call(oldValue)) {
                o.removeListener(listener);
            }
            }
            listener = o -> fireValueChangedEvent();
            for (final Observable o : extractor.call(v)) {
            o.addListener(listener);
            }
        }
        super.setValue(v);
        }
    }
    
    public class ObservableValuePropertyTest4 implements ChangeListener<Object> {
    
        @BeforeClass
        public static void setUpBeforeClass() throws Exception {
        }
    
        @AfterClass
        public static void tearDownAfterClass() throws Exception {
        }
    
        @Before
        public void setUp() throws Exception {
        }
    
        @After
        public void tearDown() throws Exception {
        }
    
        static class NestedBean {
    
        StringProperty nestedProperty = new SimpleStringProperty("hans");
    
        public static <T extends NestedBean> Callback<T, Observable[]> extractor() {
    
            return (final T o) -> new Observable[] { o.nestedProperty };
        }
    
        @Override
        public boolean equals(final Object obj) {
            if (obj instanceof NestedBean) {
            System.err.println(this.nestedProperty.get() + " " + ((NestedBean) obj).nestedProperty.get());
            return Objects.equal(this.nestedProperty.get(), ((NestedBean) obj).nestedProperty.get());
            }
            return false;
        }
    
        }
    
        private ObservableValueProperty<NestedBean> p;
    
        private NestedBean nestedBean;
    
        private String newNestedValue = null;
    
        @Test
        public void test01() {
        p = new ObservableValueProperty<>(NestedBean.extractor());
        nestedBean = new NestedBean();
        p.setValue(nestedBean);
        p.addListener(this);
        nestedBean.nestedProperty.set("peter");
        assertEquals("peter", newNestedValue);
        }
    
        @Override
        public void changed(final ObservableValue<? extends Object> observable, final Object oldValue,
            final Object newValue) {
        System.err.println("Changed");
        newNestedValue = nestedBean.nestedProperty.get();
    
        }
    
    }
    

    不幸的是,这不会引发任何更改事件,因为 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() 该值已更改,并且新值和旧值相等,因此不会通知侦听器。

        3
  •  1
  •   Claude Bouchard    7 年前

    • 我创建了一个名为 ObjectXProperty<E> ObjectProperty<E>
    • 它具有可以接受 Callback<E,Observable[]> ,我们的提取器功能;
    • 内部 ObjectXProperty SimpleObjectProperty 这削弱了所有方法;
    • 魔术在于 set(E value) 方法:我创建一个 ObjectBinding 只需将 ,但它使用提取器函数来决定何时失效!

    • 如果 bind 方法之前在 ,让“真正的”装订完成他的工作;如果 unbind 方法被调用;

    这是我的新课 :

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

    }

        4
  •  -1
  •   karel Aadil Minhaz    6 年前

    我认为你需要给你的对象添加一个监听器。这可以简单地做到。首先,您应该用构造函数和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.
        });