Custom UI Controls with JavaFX (Part3)
In this post I will explain the basic JavaFX Property API because most of the planed future tutorials will depend on Properties. I will first explain the old Swing way to better understand the idea of the new API and all problems that are solved by it.
Properties in Swing
In Swing every Component is a JavaBean and its fields (bean properties) are accessible by getters and setters. If you want to change the text of a button you have to call the setter method:
With this methods you can easily modify and control your user interface in a static way. For example you can receive and check all entered data by calling the required getter methods on your components once the save button is pressed.
But in the last years user experience has changed. Data should be validated in the moment it has been entered by the user and the background color of a application should change on the fly while the user is dropping the mouse over the colorchooser. All this things can be done with Swing and PropertyChangeSupport. In a well coded Swing Control every change of a bean property fires a PropertyChangeEvent to all its listeners. The official Swing components offer this feature for all properties. Here is a short example that shows
PropertyChangeSupport in a custom JComponent:
It’s quite simple to access this feature outside of the component:
The problem is that all this stuff produces a lot of redundant code and do not last for modern and big applications.
Glue code and BeansBinding
Most of the time
PropertyChangeSupport is used in Swing applications to connect the view to a data model. By doing so a lot of code looks like this:
This code is really painful and vulnerable to (copy & paste) errors. Thanks to the BeansBinding API the modern Swing developer do not need this glue code and simply bind properties. The API allow to bind two different properties with each other so that all changes are automatically adopted by the opposite side. Here is a short example of a binding:
With the API you can create one way or bidirectional bindings. The difference is that in a one way binding only one side is a observer. In a bidirectional binding every change on any side is adopted by the opposite side.
JavaFX includes the
javafx.beans.property.Property Interface that extend property handling and binding with some great features and a very simple but powerful API. All JavaFX controls use the property API to grant access to their fields. Normally next to the getter & setter methods there is a new method to access the property. Here is a example for the “double cellWidth” attribute of GridView:
As you can see there is no “double cellWidth” field in the code. Instead of this the attribute is wrapped in a Property. JavaFX offers a set of basic property classes for primitive datatypes like String or double. All this basic implementations are part of the package
The getter and setter methods work directly with the Property instance and set or request the current value from the property. Next to all this
Simple*Property classes there are some special implementations like read only implementations that can be used if you want to prevent your field from external changes. In this case only removing the setter-method is not enough because you can access the Property instance. It’s recommend to use
ReadOnly**Property classes (like
ReadOnlyDoubleProperty) in this case.
The benefit of Properties
By using the above described design for bean properties in JavaFX you will get a lot of pros in your code. First of all JavaFX properties offer support for
ChangeListener. So you can add listeners to every property:
The usage of
ChangeListener in JavaFX is equivalent to PropertyChangeSupport in Swing. But in my eyes there are some benefits. The code looks much cleaner and it’s very easy to add a listener to one specific field / property. In Swing you have to add the field name as a String parameter and produce plenty of refactoring risk. Next to the ChangeLister you can register
InvalidationListener to your properties. You can read more about the difference between this two listener types here.
Let’s bind this stuff
Another and in my opinion the best feature of JavaFX properties is the binding function. For this the Property interface offers the following methods:
By using this methods you can create bindings between JavaFX properties very easy and with much cleaner code than in Swing. In the following example the value of a slider will be bound to the cell width of a grid:
By doing so the change of the slider will directly change the cell width of the grid because this property is bound to the value property of the slider. You can see the result in a video (0:20):
By using the
bind(..) method a change of the column width will not influence the slider value because we have a one way binding. But creating a bidirectional binding is easy as pie:
Now whatever slider is changed, the other one will adopt it’s value:
You can simply remove a binding by calling
property.unbind() in your code.
With this methods you can easily bind two or more properties with the same value type (String, double, etc.). But sometimes you need a more complex binding. Maybe you need to bind a slider value to the visible property of a label. The label should appear once the slider value reaches a maximum. The JavaFX Property API offers some conversion methods for this needs. Most property types provides specific methods that create a new binding. Here is an sample that uses some of this methods:
In line 3 the valueProperty in converted to a new double binding that is always double the size of the wrapped property. Now by calling the greaterThan(..) method we create a boolean binding that is wrapped around the double binding. This bindings value is true while the wrapped value is > 100. So if the value of the slider is greater than 50 (50 * 2 > 100) the label will be visible:
Next to this functions there is the util class
javafx.beans.binding.Bindings that provides a lot of additional functions and support. For example you can add converters to a binding by using this class:
Once you set the value of the StringProperty to “8” the IntegerProperty will update it’s wrapped value to 8 and vice versa.
You can read more about the Property API & binding here.
Properties for custom controls
For custom components in JavaFX it is highly recommend to provide properties for all attributes of the control. By doing so you and other developers can easily bind this attributes to other properties or add change listener to them. Many JavaFX APIs (basic & 3rdParty) support the Property API. For example the next release of DataFX will provide properties for all received data so that you can easily bind your control attributes to the data that will be loaded in background. So you can bind the items inside a ListView to the result of a REST request with only one line of code.
One of my next tutorials will show how you can bind your custom control properties to CSS properties.