Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with Property<T>, a change listener and initialization of the property?

I am using JavaFX's Property<T> class and I am quite happy with the result, minified example code:

public CircularListCursor<E> {
    private final Property<E> elementProperty;

    public CircularListCursor() {
        this.elementProperty = new SimpleObjectProperty(/*some value*/);
    }

    //various methods that call elementProperty.setValue(/*some value*/);
}

Usage:

private final CircularListCursor<SelectionData> selectionDataCursor;

...

selectionDataCursor.elementProperty().addListener((observableValue, oldValue, newValue) -> {
    oldValue.getLabel().setStyle("-fx-text-fill: black");
    newValue.getLabel().setStyle("-fx-text-fill: red");
});

Now this works almost perfectly, but it doesn't trigger on the construction of the object. It is logical that it works that way, because the property is not bound to yet during construction, so no change event can be fired either.

But I do want to be notified of the initial value during construction to allow for clean code, is there a way to do so?

like image 781
skiwi Avatar asked Aug 12 '14 08:08

skiwi


People also ask

What is a property change listener?

public interface PropertyChangeListener extends EventListener. A "PropertyChange" event gets fired whenever a bean changes a "bound" property. You can register a PropertyChangeListener with a source bean so as to be notified of any bound property updates.

How do you use listener change?

In short, to use a simple ChangeListener one should follow these steps: Create a new ChangeListener instance. Override the stateChanged method to customize the handling of specific events. Use specific functions of components to get better undemanding of the event that occurred.

What is addListener in Java?

addListener. void addListener​(ChangeListener<? super T> listener) Adds a ChangeListener which will be notified whenever the value of the ObservableValue changes. If the same listener is added more than once, then it will be notified more than once.


2 Answers

There is no direct solution for that in JavaFX.

Nevertheless, you can make things a little bit easier/cleaner by moving the listener code into a private event handler method. This method can then be called once at the end of construction to initialize your object state. Thanks to Java 8 lambda expressions, you can use the method reference to the event handler method directly as listener:

// register event handler method    
selectionDataCursor.elementProperty().addListener(this::onElementChanged);

// call listener once for initialization:
onElementChanged(selectionDataCursor.elementProperty(), null, selectionDataCursor.getElement());

...

// event handler method
private void onElementChanged(ObservableValue<? extends E> observableValue, E oldValue, E newValue) {
    if (oldValue != null) oldValue.getLabel().setStyle("-fx-text-fill: black");
    if (newValue != null) newValue.getLabel().setStyle("-fx-text-fill: red");
}

Side note: Listeners built via method references can't be removed any more. More specific, the following code will NOT remove the listener, as this::onElementChanged will create a new listener every time that is not equal to the one that is already registered:

selectionDataCursor.elementProperty().removeListener(this::onElementChanged);
like image 120
isnot2bad Avatar answered Sep 28 '22 05:09

isnot2bad


Using EasyBind, you can

  1. Select the nested styleProperty from elementProperty.
  2. Bind the nested styleProperty to some observable string (in your case, a constant string for red text fill).
  3. Provide an additional string argument to the bind method that is used to reset the style property of the old element when the element changes.

Here is the code:

ObservableValue<String> constRed = new SimpleStringProperty("-fx-text-fill: red");
EasyBind.monadic(selectionDataCursor.elementProperty())
        .selectProperty(e -> e.getLabel().styleProperty())
        .bind(constRed, "-fx-text-fill: black");

Notice how you don't need to register any listeners—one binding does it all. A binding is more declarative, while a listener is more imperative (side-effectful).

like image 28
Tomas Mikula Avatar answered Sep 28 '22 04:09

Tomas Mikula