Build the Reactive system using the JavaFX tool

  • 2020-05-05 11:16:52
  • OfStack

JavaFX is the new standard library for building graphical applications in Java, but many programmers still stick with Swing and even AWT. Here are some tips on how to build responsive, fast applications that take advantage of the new awesome features in the JavaFX toolset!

1. Attribute value

If you've done a full understanding of the JavaFX component, move across the property (Property) thing. Almost every value in the FX library can be observed, the width of the partition (divider), the size of the image, the text in the text id (label), the children in a list, and the state of the check box (checkbox). There are other types of properties: writable and readable. Writable values can be modified using the setter method or directly. JavaFX handles the event handling process and ensures that every component that depends on this property is notified. A readable property has methods that let you receive notifications when its value is changed.

Example:


//  Can be read - And can write 
StringProperty name = new SimpleStringProperty("Emil"); 
//  read-only 
ObservableBooleanValue nameIsEmpty = name.isEmpty();

2. Binding value

When you have a writable and readable value, you can begin to define rules on how those values relate to each other. A writable property can be bound to a readable property so that its value always matches the readable one. Bindings don't happen immediately, but they happen before the values are observed (see what I did there). Bindings can be one-way or bidirectional. Of course, if they are bidirectional, you need both properties to be writable.

Example:


TextField fieldA = new TextField();
TextField fieldB = new TextField();
fieldA.prefWidthProperty().bind(fieldB.widthProperty());

3. List of observables

Properties are not the only things that can be observed. If the list is encapsulated in an ObservableList, then the members of the list can also be observed. ObservableList's response model is quite advanced. Not only will you be notified when the list has been modified, but you will also be able to see how the list has been modified.

Example:


List<String> otherList = Arrays.asList("foo", "bar", "bar");
ObservableList<String> list = FXCollections.observableList(otherList);

list.addListener((ListChangeListener.Change<? extends String> change) -> {
  System.out.println("Received event.");
  while (change.next()) {
    if (change.wasAdded()) {
      System.out.println(
        "Items " + change.getAddedSubList() + " was added.");
    }

    if (change.wasRemoved()) {
      System.out.println(
        "Items " + change.getRemoved() + " was removed.");
    }
  }
});

System.out.println("Old list: " + list);
list.set(1, "foo");
System.out.println("New list: " + list);

The running output of the above code is as follows:


Old list: [foo, bar, bar]
Received event.
Items [foo] was added.
Items [bar] was removed.
New list: [foo, foo, bar]

As you can see, the setup action only triggers an event once.

4. StringConverter
Sometimes you find yourself creating a binding without having to extract the value from a component. A typical example of this is that you have an StringProperty with path from a text field (TextField). If you want to have an observable property with this value represented as Path, you need to create an StringConverter for it.
Example:


TextField fileLocation = new TextField();
StringProperty location = fileLocation.textProperty();
Property<Path> path = new SimpleObjectProperty<>();
 
Bindings.bindBidirectional(location, path, new StringConverter<Path>() {
  @Override
  public String toString(Path path) {
    return path.toString();
  }
 
  @Override
  public Path fromString(String string) {
    return Paths.get(string);
  }
});

Object properties are not bidirectional binding to the values of the text field.

5. Expressions

Using the Bindings class above, you can create expressions of any type. Let's say you have two text fields that allow the user to enter information. Now you want to define a read-only field that will always contain a string, and if the length of the two strings is the same, the two strings will be mixed up and displayed character by character. If the length is not equal, a help message is displayed.
Example:


TextField first = new TextField();
TextField second = new TextField();
TextField mix  = new TextField();
 
mix.textProperty().bind(
  Bindings.when(
    first.lengthProperty().isEqualTo(second.lengthProperty())
  ).then(Bindings.createStringBinding(
    () -> {
      int length    = first.lengthProperty().get();
      String firstText = first.textProperty().get();
      String secondText = second.textProperty().get();
      char[] result   = new char[length * 2];
 
      for (int i = 0; i < length; i++) {
        result[i * 2]   = firstText.charAt(i);
        result[i * 2 + 1] = secondText.charAt(i);
      }
 
      return new String(result);
    }, 
    first.textProperty(),
    second.textProperty()
  )).otherwise("Please enter two strings of exactly the same length.")
);

Here are just a few of JavaFX's many features. Hope you can find more creative ways to use this event system!


Related articles: