Tokee's

Sehen, wozu das führt…

Get notified when working with JavaFX properties

| 6 Kommentare

When working with JavaFX properties most of us are interested in notifications about value changes. Getting notified about any of these changes can be realised by applying a ChangeListener or an InvalidationListener. We’re fine so far, aren’t we?
(In case of you’re not familiar with using JavaFX properties, start with reading Using JavaFX Properties and Binding at Oracle’s website.)

Have a look at the following example:

package propertyexample;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class PropertyExample {

    private StringProperty value;

    public PropertyExample() {
        this.value = new SimpleStringProperty(this, "value");
    }

    public StringProperty getValueProperty() {
        return this.value;
    }
    
    public final void setValue(String stringValue) {
        this.value.set(stringValue);
    }
    
    public final String getValue() {
        return this.value.get();
    }
    
    public static void main(String[] args) {
        final PropertyExample example = new PropertyExample();
        
        example.getValueProperty().addListener(new InvalidationListener() {

            @Override
            public void invalidated(Observable o) {
                StringProperty property = (StringProperty)o;
                System.out.println("current value: " + property.getValue());
            }
        });
              
        //assign String instance with a value of 'a' and fire InvalidationListener
        example.setValue(new String("a")); 
        //assign String instance with a value of 'a' and nothing will happen
        example.setValue(new String("a"));
    }
}

As you might have expected the applied InvalidationListener will only fire once, since new String("a").equals(new String("a")) => true. This is fine for most cases, but what when you’re interested in getting notified when any value is assigned and expect the InvalidationListener firing everytime a value is assigned, even if the new value equals the previously assigned one. I spent much time and tried many different ways

  • extending the SimpleStringProperty
  • implementing a new StringProperty and
  • fallback to good old listeners

All of these ways lead to a working solution, but the expense of time and lines of code is beyond any comparison with my finally working solution. This one is as simple as just exchanging the StringProperty with an ObjectProperty<String>. This works since value comparison and marking as invalid is implemented differently in SimpleStringProperty and SimpleObjectProperty<String>.

package propertyexample;

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

public class PropertyExample {
    
    private ObjectProperty<String> value;
    
    public PropertyExample() {
        this.value = new SimpleObjectProperty<String>(this, "value");
    }
    
    public ObjectProperty<String> getValueProperty() {
        return this.value;
    }
    
    public final void setValue(String stringValue) {
        this.value.set(stringValue);
    }
    
    public final String getValue() {
        return this.value.get();
    }
    
    public static void main(String[] args) {
        final PropertyExample example = new PropertyExample();
        
        example.getValueProperty().addListener(new InvalidationListener() {
            
            @Override
            public void invalidated(Observable o) {
                ObjectProperty<String> property = (ObjectProperty<String>) o;
                System.out.println("current value: " + property.get());
            }
        });
        
        //assign String instance with a value of 'a' and fire InvalidationListener
        example.setValue(new String("a"));
        //assign String instance with a value of 'a' and fire InvalidationListener
        example.setValue(new String("a"));
    }
}

Just a note: To get notified you have to assign different references. Assigning the same reference as shown in the following example only results in firing the InvalidationListener once.

String a = new String("a");
example.setValue(a);
example.setValue(a);

But this shouldn’t be a hurlde ;)

6 Kommentare

  1. Are you sure it is perfectly fine to use JavaFX classes outside JavaFX? (and outside JavaFX application thread)

    Recently i found a blog where someone was using the properties like that and he had a NotOnJavaFxThread exception and was blaming JavaFX for this. But I think that this is a bad practice and certainly a bad example of using JavaFX.

    • I don’t know the other blog post, but in my personal opinion the usage of JavaFX should be limited to UI purposes since it’s a UI technology.
      Using properties for model->view communication is perfectly fine. Sourcecode used in this blog post should be considered as example-code.

      • I mean, you run this code outside FX Application thread. Yes, in this example it is not crucial. But certainly worth to mention.

      • To run on the JavaFX Application thread either

        extend Application and run from start() {}

        OR

        Platform.runLater(new Runnable(){
        public void run(){
        // this executes on JavaFX Application thread.
        // You can even update UI from here.
        }
        });

  2. And by the way, the tip about only one invalidation event was helpful for me. Nice blog, keep it rolling :)

Hinterlasse eine Antwort

Pflichtfelder sind mit * markiert.