Properties and Bindings in JavaFX

JavaFX 2 brings the Properties and Bindings concepts. I will do a little overview of the possibilities offered by this two mechanisms and give you a quick example of how you can use them while building a JavaFX UI.

Properties

Properties allow you to expand and improve the well known JavaBean concept. Indeed it brings you a way to “observe” a value of bean and to have a full event delivery system. That means you will be able to perform an action when the value changes. Let’s see a simple bean:

public class Rectangle {

  private double width;
  private double height;
  private double surface;

  public double getWidth() { return this.width; }
  public void setWidth(double width) { this.width = width; } 
  public double getHeight() { return this.height; }
  public double setHeight(double height) { this.height = height; }
  public double getSurface() {
    this.surface = this.getWidth() * this.getHeight();
    return this.surface;
  }
}

Now we’re going to just create a main that will be used to display the values of a rectangle.

public class Runner {
  public static void main(String ... args) {
    Rectangle rect = new Rectangle() {{
      setWidth(100);
      setHeight(200);
    }};

    System.out.println("WIDTH: " + rect.getWidth());
    System.out.println("HEIGHT: " + rect.getHeight());
    System.out.println("SURFACE: " + rect.getSurface());
  }
}

In order to transform this bean to use Properties, you will replace the type of each field by the corresponding type in Properties. For example (there are more …):

Type Proerties type
int IntegerProperty
double DoubleProperty
short ShortProperty
String StringProperty
Object ObjectProperty<T>

You also have to respect some conventions like:

  • having public final getter/setter for each field
  • having public getter for the property

Let’s make the changes to our bean:

public class Rectangle {

  private DoubleProperty width;
  private DoubleProperty height;
  private DoubleProperty surface;

  public DoubleProperty widthProperty() {
    if(this.width == null) { this.width = new SimpleDoubleProperty(); }
    return this.width;
  }
  public final double getWidth() { return this.widthProperty().get(); }
  public final void setWidth(double width) { this.widthProperty().set(width); }

  public DoubleProperty heightProperty() {
    if(this.height == null) { this.height = new SimpleDoubleProperty(); }
    return this.height;
  }
  public final double getHeight() { return this.heightProperty().get(); }
  public final void setHeight(double height) { this.heightProperty().set(height); }

  public DoubleProperty surfaceProperty() {
    if(this.surface == null) { this.surface = new SimpleDoubleProperty(); }
    return this.surface;
  }
  public final double getSurface() {
    this.surfaceProperty().set(this.getWidth() * this.getHeight());
    return this.surfaceProperty().get();
  }
}

As you can see, the getters for the properties don’t match classic convention we’ve known for a JavaBean. Indeed the name of the getter is always like: <fieldName>Property.

Properties and events

Like I said you can “listen” for changes on a property. It may be useful to update a part of your software, tracing a log, etc. In order to do this you can choose to add a listener to a property. Listeners can be of two types:

  • ChangeListener: to quickly explain it, you have old and new value of the property
  • InvalidationListener: to quickly explain it, you just know the value was changed

So for example, let’s add a listener to the surface property:

// ...
public DoubleProperty surfaceProperty() {
  if(this.surface == null) {
    this.surface = new SimpleDoubleProperty();
    this.surface.addListener(new ChangeListener<Number>() {
      @Override
      public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
        System.out.println("The value was changed from " + oldValue.toString() + " to " + newValue.toString());
      }
    });
  }
  return this.surface;
}
// ...

Now each time the value changes, the message will be displayed.

Bindings

The rectangle example with the surface wasn’t chosen for nothing. Indeed it will serves as demonstration of the Bindings concept of JavaFX. The surface is always the width multiplied by the height. In our example we made the update of the surface manually but we don’t need to, thanks to the Bindings API. You have three major bindings:

  • Unidirectional binding
  • Bidirectional binding
  • Number binding

Unidirectional binding

You can bind a property p1 to another p2, meaning when p2 is modified, p1 will be updated with the new value of p2.

// Useful for a square
this.widthProperty().bind(this.heightProperty());

Bidirectional binding

You can bin two properties one to each other meaning when one is updated, the other will be too.

this.widthProperty().bindBidirectional(this.heightProperty());

Number binding

In our example, the surface should be updated if the width and/or height is updated, and the surface is the multiplication of the width and height. We will use a number binding.

NumberBinding multiplication = Bindings.multiply(this.widthProperty(), this.heightProperty());
this.surfaceProperty().bind(multiplication);

The number binding will be updated each time the width and/or height is updated, and because we bind the surface property to it, the surface will be updated as well. You can update the surfaceProperty() method by this:

public DoubleProperty surfaceProperty() {
  if(this.surface == null) {
    this.surface = new SimpleDoubleProperty();
    this.surface.addListener(new ChangeListener() {
      @Override
      public void changed(ObservableValue observable, Number oldValue, Number newValue) {
        System.out.println("The value was changed from " + oldValue.toString() + " to " + newValue.toString());
      }
    });
    this.surface.bind(Bindings.multiply(this.widthProperty(), this.heightProperty()));
  }
  return this.surface;
}

public final double getSurface() {
  return this.surfaceProperty().get();
}

Using Properties and Bindings in a UI

Do you remember the old times, when we were building some Swing UI? When the app was resized, you had to compute the new size of the components. Now things have changed, in a much better way with JavaFX! Indeed, every component in JFX is built using Properties: widthProperty(), heightProperty(), visibleProperty(), layoutXProperty(), layoutYProperty(), styleProperty(), etc etc etc.
So if you need to update the size of a particular component which depends on the size of another one, no problem ! You can bind properties using NumberBinding if you want to do a ratio for example, or (bi | uni)directional binding.
The concept is very powerful and should remember you the Observer/Observable pattern we use to have in JSE.

This was a quick overview of Properties and Bindings, now it’s time for you to code.

About these ads

3 Responses to Properties and Bindings in JavaFX

  1. Pingback: Java desktop links of the week, July 30 | Jonathan Giles

  2. Pingback: JavaFX links of the week, July 30 // JavaFX News, Demos and Insight // FX Experience

  3. Pingback: TableView: display tables in JavaFX « System.out.println("Thierry WASYL : Java blog")

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: