javafxpropertiesbindingobject-property

Is it possible to make a property which listen to changes of custom objects in javafx?


Let's assume I have an entity class Foo which contains some fields, getters, setters and constructor. For example:

public class Foo {
private Integer a = 0;
private Integer b = 0;

public Foo() {
}

public Integer getA() {
    return a;
}

public void setA(Integer a) {
    this.a = a;
}

public Integer getB() {
    return b;
}

public void setB(Integer b) {
    this.b = b;
}
}

Then i want to know when a or b changes. I know that there is an ObjectProperty in javaFX. So i'm creating Object Property:

ObjectProperty<Foo> fooProperty = new SimpleObjectProperty<>(new Foo());

Then in order to know about changes of a and b fields, I add ChangeListener:

fooProperty.addListener((observable, oldValue, newValue) -> {
    System.out.println("Property changed!");
});

Then experiments:

fooProperty.set(new Foo());

working well, but next line:

fooProperty.get().setA(10)

does not invoke listener. The reason for that is clear. Last line actually doesn't change the Foo object (reference remains the same).

I have some possible solutions. But all of them are not elegant enough. For example changing each field as a property and adding the same listener for each variable.

So, the question is. What is the way to add listener for changes of the fields of custom object?


Solution

  • do not know if this what you want or not, you may take it for reference.

    First i create a class, its function is to carry information.

    public class EventObj{
      private String message;
      private Object newObj;
    
      public EventObj(String msg, Object obj){
        this.message = msg;
        this.newObj = obj;
      }
    
      public String getMessage(){
        return this.message;
      }
    
      public Object getNewObj(){
        return this.newObj;
      }
    }
    

    then, create the Foo class, it has two Object fields, we are going to listen if they change.

    public class Foo{
      private SimpleObjectProperty<Object> a;
      private SimpleObjectProperty<Object> b;
    
      private SimpleObjectProperty<EventObj> out;
    
    
      public Foo(){
        a = new SimpleObjectProperty<>();
        b = new SimpleObjectProperty<>();
        out = new SimpleObjectProperty<>();
        initEvent();
      }
    
      private void initEvent(){
        a.addListener(new ChangeListener<Object>(){
          @Override
          public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue){
            out.set(new EventObj("a changed", newValue));
          }
        });
    
        b.addListener(new ChangeListener<Object>(){
          @Override
          public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue){
            out.set(new EventObj("b changed", newValue));
          }
        });
      }
    
      public void setA(Object a){
        this.a.set(a);
      }
    
      public void setB(Object b){
        this.b.set(b);
      }
    
      public SimpleObjectProperty<EventObj> outProperty(){
        return out;
      }
    }
    

    in other class, using the code below, to listen if the a or b in foo has change.

    Foo foo = new Foo();
    foo.outProperty().addListener(new ChangeListener<EventObj>(){
      @Override
      public void changed(ObservableValue<? extends EventObj> observable, EventObj oldValue, EventObj newValue){
        System.out.println(newValue.getMessage() + ", new value is : " + newValue.getNewObj());
      }
    });
    
    foo.setA(new Object());
    foo.setB(new Object());
    

    in this case, we just use Object class, if you wanna use a custom class to replace, don't forget to override its hashCode() and equals() methods to determine whether the two objects are the same.