Introduction

I’m not a big fan of the way that the JavaFX JavaDocs are written, especially the introductory sections. My biggest complaint is that they are relatively opaque to a reader with little understanding of the specific topic. Quite often, I’ll go back once I have a fuller understanding of a concept and I’ll see that the introductory discussion actually directly addressed whatever issue I was having, just not in a way where I had any hope of picking up on it at the beginning.

Let’s start by looking at the code that was included in the introductory discussion for PseudoClass:

public boolean isMagic() {
       return magic.get();
   }

   public BooleanProperty magicProperty() {
       return magic;
   }

   public BooleanProperty magic =
       new BooleanPropertyBase(false) {

       @Override protected void invalidated() {
           pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get());
       }

       @Override public Object getBean() {
           return MyControl.this;
       }

       @Override public String getName() {
           return "magic";
       }
   }

   private static final PseudoClass
       MAGIC_PSEUDO_CLASS = PseudoClass.getPseudoClass("xyzzy");

Right off the bat, this is probably the most complicated way that you could implement a new PseudoClass, and it’s written under the unspoken assumption that this code would be inside some custom Node class that implements the PseudoClass. The only way that you can determine that last assumption is because the call to pseudoClassStateChanged() has an implied this and that pseudoClassStateChanged() is a member method of Node. So this has to refer to Node which means that this anonymous inner class has to be defined inside a custom class that extends Node.

But, putting that aside, the really interesting thing here is this part:

@Override protected void invalidated() {
    pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get());
}

Woa! What’s this?

Well, it’s in the JavaDocs:

The method invalidated() can be overridden to receive invalidation notifications. This is the preferred option in Objects defining the property, because it requires less memory. The default implementation is empty.

That doesn’t help much, to be honest. The memory stuff is confusing too.

The other thing to notice is that this is in BooleanPropertyBase. It’s also in IntegerPropertyBase, ObjectPropertyBase, and, I assume, all the ...PropertyBase classes. But what are these classes?

To understand this, let’s first look at SimpleBooleanProperty, the one we’re all familiar with…

SimpleBooleanProperty

Here’s the source code for SimpleBooleanProperty:

public class SimpleBooleanProperty extends BooleanPropertyBase {
    private static final Object DEFAULT_BEAN = null;
    private static final String DEFAULT_NAME = "";
    private final Object bean;
    private final String name;

    public Object getBean() {
        return this.bean;
    }

    public String getName() {
        return this.name;
    }

    public SimpleBooleanProperty() {
        this(DEFAULT_BEAN, "");
    }

    public SimpleBooleanProperty(boolean var1) {
        this(DEFAULT_BEAN, "", var1);
    }

    public SimpleBooleanProperty(Object var1, String var2) {
        this.bean = var1;
        this.name = var2 == null ? "" : var2;
    }

    public SimpleBooleanProperty(Object var1, String var2, boolean var3) {
        super(var3);
        this.bean = var1;
        this.name = var2 == null ? "" : var2;
    }
}

Really what we have here is 4 different constructors, all designed to handle various cases of defining, or not defining, the Java Bean, the name and the initial value of the Property. Other than that, it doesn’t do much, and it delegates any initialization of the boolean value to its parent, BooleanPropertyBase.

If you look at the abstract BooleanPropertyBase class, you’ll find that it does 100% of the actual Property stuff you need, but none of the Java Bean handling at all. Those functions are left to whatever concrete class extends from it. So SimpleBooleanProperty only introduces enough code to support the Java Bean functions getName() and getBean() required by the interface ReadOnlyProperty that is implemented by BooleanProperty.

I’ll admit that the Java Bean stuff in Properties mystifies me a little bit. I’ve always assumed that it was used for serialization and de-serialization, but I’ve yet to see a real case for serializing JavaFX properties. I’ve certainly never seen any project where the Java Bean values were actually used for JavaFX Properties.

But still, this is what SimpleBooleanProperty (and all the other “Simple…Property” classes) add to BooleanPropertyBase, and therefore, BooleanProperty.

And that means that there’s nothing wonderful or mysterious or essential about the Simple...Property classes at all. They just add some simple stuff to ...PropertyBase. There’s no inherent advantage to extending Simple...Property classes instead than ditching them and extending directly from ...PropertyBase - as the example from the PseudoClass JavaDocs does.

Creating a Custom PseudoClassProperty

Let’s look at creating a custom Property to handle PseudoClass changes that we can bind to other Properties. We’ll build it as an extension of BooleanPropertyBase. It’s going to be a BooleanProperty because that’s what we need for PseudoClasses, they’re either off or on, true or false.

We’ll call our new class: PseudoClassProperty.

The only things that we are obligated to handle, because they are defined in the interfaces but not implemented in BooleanPropertyBase or any class it inherits from, are getBean() and getName(). Remember, we’re not obligated to use any structure or particular values under the hood, we just have to implement these two methods.

It turns out that there are two pieces of information we’ll need to supply when we instantiate our PseudoClassProperty: The Node to which it is associated, and the PseudoClass that it implements. It’s not a stretch to view the Node as a good candidate for the Bean, since it kind of “owns” the Property. The PseudoClass also has a name, which would work as the PseudoClassProperty name as well. Maybe, if the Node also has an id defined, we would want to combine it with the PseudoClass name to became the Property name.

That being said, I don’t actually see any practical use to calling either getBean() or getName() in the real world. So this exercise is a little bit like “going through the motions”, but at least we’ll have return values that are sensible.

This solves all of our constructor issues. We only need one kind of constructor since we always need to supply the Node and the PseudoClass, and we can let Kotlin handle a default value for our initial value of the Property, making it optional. In truth, any instance of PseudoClassProperty would almost inevitably be bound to some other Property right away, and the initialValue parameter could probably just be taken out of the constructor. But with Kolin, it doesn’t hurt to leave it in (Yeah! Kotlin!!!)

This gives us this:

class PseudoClassProperty(private val node: Node, private val pseudoClass: PseudoClass,
                          initialValue: Boolean = false) : BooleanPropertyBase(initialValue) {
    override fun getBean() = node
    override fun getName(): String = pseudoClass.pseudoClassName

    override fun invalidated() {
        node.pseudoClassStateChanged(pseudoClass, value)
    }
}

This is geunuinely, generically useful.

There is virtually no way to implement a PseudoClass that’s going to be linked to a Property that isn’t going to execute node.pseudoClassStateChanged(pseudoClass, value) in some way. It’s total “boilerplate”, and code that you want out of your layout code.

There’s a little bit of added Kotlin trickery that can be used here to:

infix fun Node.addPseudoClass(pseudoClass: PseudoClass) = PseudoClassProperty(this, pseudoClass)

What does this do? It adds a function to virtually every JavaFX component that connects a PseudoClassProperty to it. Now we can do this:

private fun createSampleRegion() = HBox(10.0).apply {
   children += Label("Some Text").apply {
      addPseudoClass(somePseudoClass).bind(model.someBooleanProperty)
  }
}

The Action Property

The whole point of PseudoClassProperty is that it connects something that doesn’t know about Observables into something that does know about Observables through an “action”. In this case, it’s the PseudoClass state change that now responds to a state change in an ObservableBooleanValue. Generally speaking this is an “adaptor”.

It’s what I’m calling an “Action Property”.

And it’s possible because of ...PropertyBase.invalidated(). But what is that?

…PropertyBase.invalidated()

Let’s look at the source code for BooleanPropertyBase. We find this:

private void markInvalid() {
    if (this.valid) {
        this.valid = false;
        this.invalidated();
        this.fireValueChangedEvent();
    }

}

protected void invalidated() {
}

public void set(boolean var1) {
    if (!this.isBound()) {
        if (this.value != var1) {
            this.value = var1;
            this.markInvalid();
        }

    } else {
        String var10002 = this.getBean() != null && this.getName() != null ? this.getBean().getClass().getSimpleName() + "." + this.getName() + " : " : "";
        throw new RuntimeException(var10002 + "A bound value cannot be set.");
    }
}

First off, we see that, just as described in the JavaDocs, invalidated() is empty. So we’ll never need to call super.invalidated().

More importantly: Since it’s empty, that means that it’s only reason for existance is to be overriden in extending classes! Keep that in mind.

We can also see that invalidated() is called by markInvalid(). Looking a little bit closer, we can see that markInvalid() is called by the public method set() when the value actually changes. There are a few other places that markInvalid() is called from which have to do with handling when it is bound to another Property. But you should be able to get the idea from set().

Back in days long gone by, this is something that we would have called a “hook”. It’s an empty method that is called in certain circumstances with the expectation that you can, if you want, supply some code to run there in an extending class. Its sole purpose is to provide that facility.

Advantages of invalidated() Over addListener(InvalidationListener)

Let’s be fair. There’s nothing you can do with overriding invalidated() that you can’t do with addListener(). There are no other protected methods or fields all the way up the hierarchy that you could only access through invalidated(). So no special status is awarded to code running in invalidated().

The difference is boilerplate code in your layout.

Animations

Another place in JavaFX where you simple cannot avoid actions is Animations and Transitions. These need to be launched by imperative code, and there’s no way around it. However, you can use an Action Property to run a Transition. Then you have a bindable Property that will automatically run the Transition when whatever it is bound to changes.

Let’s take a look at an example that runs a FadeTransition whenever the visibility of a Node is changed:

class FadeActionProperty(private val node: Node, private val duration: Duration) : BooleanPropertyBase() {
    override fun getBean() = node
    override fun getName() = "Fade Action Property"

    private val transition = FadeTransition(duration, node)

    override fun invalidated() {
        if (value) node.isVisible = true
        transition.interpolator = Interpolator.EASE_IN
        transition.toValue = if (value) 1.0 else 0.0
        transition.setOnFinished { node.isVisible = value }
        transition.playFromStart()
    }
}

infix fun Node.addFade(duration: Duration): FadeActionProperty = FadeActionProperty(this, duration)

fun <T : Node> T.bindFade(duration: Duration, boundTo: ObservableBooleanValue): T =
    apply { addFade(duration).bind(boundTo) }

class ActionPropertyViewBuilder1() : Builder<Region> {
    private val nodeVisible: BooleanProperty = SimpleBooleanProperty(true)

    override fun build(): Region = VBox(10.0).apply {
        children += VBox(
            10.0,
            Label("Line 1"),
            Label("Line 2").bindFade(Duration.millis(700.0), nodeVisible),
            Label("Line 3"),
        )
        children += ToggleButton("Visible").apply {
            nodeVisible.bind(selectedProperty())
            isSelected = true
        }
        minWidth = 300.0
    } padWith 20.0
}

class ActionPropertyApplication1 : Application() {
    override fun start(stage: Stage) {
        with(stage) {
            scene = Scene(ActionPropertyViewBuilder1().build())
            show()
        }
    }
}


fun main() {
    Application.launch(ActionPropertyApplication1::class.java)
}

At the top of this we have our new Action Property, FadeActionProperty. Then we have a couple of convenience extension functions to make this easier to use in a layout. The FadeTransition will have a toValue of either 0.0 or 1.0 depending on whether we are fading in or out - determined by the new value of the FadeActionProperty.

Our layout is just three Labels and a ToggleButton in a VBox. We instantiate our FadeActionProperty and attach it to the second Label, bound to another BooleanProperty called nodeVisible. Then nodeVisible is bound to the Selected Property of the ToggleButton. So now, when the ToggleButton is clicked, nodeVisible toggles along with it, and each toggle triggers the FadeTransition.

Our FadeActionProperty needs to be associated with a Node, which gives us our Bean, and then it’s just been named “Fade Action Property” to satisfy the interface requirements. When the value is invalidated, we set up a FadeTransition to either fade in or fade out by setting FadeTransition.toValue and then we run it.

Before we start the transition, the Node has to be visible. There’s no point in fading otherwise. Then, when the transition ends, we set the Node's Visible Property to the correct value.

One Issue With this Approach

There’s one aspect of this implementation that I do not like, but unfortunately there’s no way around it that I can find.

It’s that the setup of the fade transition has to be done externally to the Node that’s affected. What this means is that you cannot just create a Node who’s fade behaviour is strictly an internal feature that’s not exposed to the outside world. Imagine that you wanted to create some layout component who’s behaviour was to fade in and fade out, that’s just the way that it would respond to a change in its Visible Property. In this example case, the layout would simply bind the Visible Property of the Label to the Selected Property of the ToggleButton and the FadeActionProperty would be bound to the VisibleProperty. The layout would have no idea that the fade Transition would happen, it would just be part of the Label's behaviour.

The problem is the first line in the invalidated() method in the FadeActionProperty:

if (value) node.isVisible = true

If the new value of the Property is false, then the Node disappears intstantly, so you have to force it back to true. This means that you’ll have to suppress acting on the subsequent invalidation of the FadeActionProperty, but that can be dealt with. The problem occurs if node.visibleProperty() is bound to another ObseverableValue, because that line of code will fail and generate an exception because a bound value cannot be set.

At a minimum, you would need to be able to unbind the Visible Property, force it to true, run the FadeTransition and then rebind it to whatever it was bound to before. And this is where you get stuck. You can see if the Property is bound:

public boolean isBound() {
    return this.observable != null;
}

So, the value it’s bound to is called observable but you cannot see observable itself:

private ObservableBooleanValue observable = null;

And there’s no getter for observable.

This means that you can’t do something like this:

val boundTo = node.visibleProperty().observable
node.visibleProperty.unbind()
node.isVisible = true
   .
   .
   .
node.visibleProperty().bind(boundTo)

Another alternative might be to extend the Node class and then override Node.visibleProperty() so that you could insert your own proxy property to track the bindings. But Node.visibleProperty() is final. So you cannot do that either.

On the upside, while investigating this I did find that Node's Visible Property is actually an Action Property itself. Take a look:

this.visible = new StyleableBooleanProperty(true) {
    boolean oldValue = true;
    protected void invalidated() {
        if (this.oldValue != this.get()) {
            NodeHelper.markDirty(Node.this, DirtyBits.NODE_VISIBLE);
            NodeHelper.geomChanged(Node.this);
            Node.this.updateTreeVisible(false);
            if (Node.this.getParent() != null) {
                Node.this.getParent().childVisibilityChanged(Node.this);
            }
            this.oldValue = this.get();
        }
    }
    public CssMetaData getCssMetaData() {
        return Node.StyleableProperties.VISIBILITY;
    }
    public Object getBean() {
        return Node.this;
    }
    public String getName() {
        return "visible";
    }
};

The invalidated() method does all the action work associated with removing the Node from the Scene.

Encapsulation

Another use for this technique is to encapsulate some functionality with the Property itself, allowing the Property to become a complete functional unit without additional coding in your layout code.

Here’s a very simple example where a property counts the number of times that it has been changed:

class EncapsulatingProperty(initialValue: Boolean) : BooleanPropertyBase(initialValue) {
    override fun getBean(): Any = Object()
    override fun getName() = "Encapsulating Property"
    var counter = 0

    override fun invalidated() {
        println("Running invalidated()")
        counter++
    }
}

fun main() {
    val testProperty = EncapsulatingProperty(true)
    println("Counter: ${testProperty.counter} value: ${testProperty.value}")
    testProperty.value = true
    println("Counter: ${testProperty.counter} value: ${testProperty.value}")
    testProperty.value = false
    println("Counter: ${testProperty.counter} value: ${testProperty.value}")
    testProperty.value = true
    println("Counter: ${testProperty.counter} value: ${testProperty.value}")
    testProperty.value = false
    println("Counter: ${testProperty.counter} value: ${testProperty.value}")
}

If you run this, you’ll get the following output:

Counter: 0 value: true
Counter: 0 value: true
Running invalidated()
Counter: 1 value: false
Running invalidated()
Counter: 2 value: true
Running invalidated()
Counter: 3 value: false

This is a very trivial example, but it does show how you can use extension of ...PropertyBase to build custom Properties that do special things, and how invalidated() fits into that.

Conclusion

Action Properties can be used to bundle together changes in State and imperative code that needs to be triggered whenever those changes occur.

Obviously, there’s nothing that you can do with invalidated() that you cannot do with ObsevableValue.addListener() because there’s nothing special about the running environment of invalidated() that would give it some advantage. What it does do is to allow you to get irrelevant details out of your layout code.

This is a very important point. Layout code should, as much as possible, contain only code that’s specific to that particular layout. So any method that you can find that allows you to move generic and boilerplate code out of your layouts should be leveraged whenever you can.

Another important idea that you should get from this article is that there is nothing special about Simple...Property classes. The only functionality that they add is to implement two useless Java Bean methods that you’re never going to call anyway. You should feel free to ditch Simple...Property whenever you like and extend directly from ...PropertyBase to create custom Property classes that do exactly what you want, encapsulating the functionality that you want to keep out of your layout code.

Categories:

Updated: