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?
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.
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.
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.
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);
Using EasyBind, you can
styleProperty
from elementProperty
.styleProperty
to some observable string (in your case, a constant string for red text fill).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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With