Search code examples
javajavafxjavafx-11

What does the registerChangeListener method do in JavaFX?


I recently started working with JavaFX. I changed the JDK from 8 to 11 in my project and there is a class that uses this method:

public class LabeledColorPickerSkin extends ColorPickerSkin {
    public LabeledColorPickerSkin(final LabeledColorPicker colorPicker) {
        super(colorPicker);
        registerChangeListener(colorPicker.valueProperty(), "VALUE");
        text.setText(colorPicker.text);
        updateColor();
     }
}

It is used here:

import javafx.scene.control.ColorPicker;
import javafx.scene.control.Skin;

public class LabeledColorPicker extends ColorPicker {
    public LabeledColorPicker()
    {
        super();
        getStyleClass().addAll("labeled-color-picker",
                "text-visible");
    }
    /** {@inheritDoc} */
    @Override protected Skin<?> createDefaultSkin() {
        return new LabeledColorPickerSkin(this);
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    String text;
}

In version 8 the method looked like this:

protected final void registerChangeListener(ObservableValue<?> property, String reference) {
    if (changeListenerHandler == null) {
        changeListenerHandler = new MultiplePropertyChangeListenerHandler(p -> {
            handleControlPropertyChanged(p);
            return null;
        });
    }
    changeListenerHandler.registerChangeListener(property, reference);
}

And now it looks like this:

protected final void registerChangeListener(ObservableValue<?> property, Consumer<ObservableValue<?>> consumer) {
    if (lambdaChangeListenerHandler == null) {
        lambdaChangeListenerHandler = new LambdaMultiplePropertyChangeListenerHandler();
    }
    lambdaChangeListenerHandler.registerChangeListener(property, consumer);
}

I do not understand what this method does and how to rewrite this code for JavaFX 11.


Solution

  • This method is used for registering listeners to properties of the control. It makes sure only a single listener is used for this purpose in addition to making sure a WeakChangeListener is used to allow the skin to be garbage collected, if you replace it.

    In JavaFX 8 the containing class SkinBase (as well as subclasses like ColorPickerSkin) was part of the internal API and used Strings to be notified of changes of properties (yikes). When a property was changed, the handleControlPropertyChanged method was called receiving with the string passed to as second argument to the registerChangeListener method as parameter.

    The following code is from the source code of ColorPickerSkin, but the code you're working with should contain something similar:

    @Override protected void handleControlPropertyChanged(String p) {
        super.handleControlPropertyChanged(p);
    
        if ("SHOWING".equals(p)) {
            ...
        } else if ("VALUE".equals(p)) {
            /* Some logic for updating the GUI */
        }
    }
    

    To rewrite the code for the new (JavaFX 9+) API you need move the logic executed, if "VALUE" is passed as parameter to the method from this method to a Consumer<ObservableValue<?>>:

    public LabeledColorPickerSkin(final LabeledColorPicker colorPicker) {
        super(colorPicker);
        registerChangeListener(colorPicker.valueProperty(), observable -> {
            /* Some logic for updating the GUI */
        });
        text.setText(colorPicker.text);
        updateColor();
    }