Merry Xmas and a happy new year to everybody.
I hope to be able to work on a new library for JavaFX during my vacation, which I was thinking about in the last couple of days. It’s going to be a nice little thing, I think, so stayed tuned!
Merry Xmas and a happy new year to everybody.
I hope to be able to work on a new library for JavaFX during my vacation, which I was thinking about in the last couple of days. It’s going to be a nice little thing, I think, so stayed tuned!
Bindings are one of the key features of the JavaFX language, because they simplify oneΒ΄s life tremendously. By reducing the amount of boilerplate code, which is usually needed to keep the objects in an application in-sync, the developer can concentrate on the really important things. (You know, like a cool looking UI.) π
Unfortunately Java objects do not provide this capabilities for their fields. That means the boilerplate code has to be written manually. This article will show a solution to bind properties of Java objects.
LetΒ΄s assume, we have a Java class Rectangle with the four properties x, y, width, and height. We want to show the rectangle on the screen using JavaFX script and update its position and size as the properties of the Java object change.
The code of the class Rectangle would be something like this:
1 public class MyRectangle { 2 private int x, y; 3 private int width, height; 4 5 public int getX() { return x; } 6 public void setX(int x) { this.x = x; } 7 public int getY() { return y; } 8 public void setY(int y) { this.y = y; } 9 public int getWidth() { return width; } 10 public void setWidth(int width) { this.width = width; } 11 public int getHeight() { return height; } 12 public void setHeight(int height) { this.height = height; } 13 14 // ... more functionality here 15 }
Code Sample 1: First draft of MyRectangle
What we want the JavaFX script to look like, is this:
1 import javafx.stage.Stage; 2 import javafx.scene.Scene; 3 import javafx.scene.shape.Rectangle; 4 5 Stage { 6 scene: Scene { 7 // ... more attributes here 8 content: Rectangle { 9 x: bind r.x 10 y: bind r.y 11 width: bind r.width 12 height: bind r.height 13 } 14 } 15 }
Code Sample 2: JavaFX program binding the rectangle
This looks simple enough and the only question remaining is: what is βrβ? To allow the script to bind to the fields of the Java object, we need to introduce an adapter between the two, which provides a more JavaFX-like interface of the Java class and which will take care of the synchronization. A simplified class-diagram of the three objects is shown in Figure 1:
Class Diagram
The basis interface of the adapter can be defined as follows:
1 public class RectangleAdapter { 2 public-read var x: Integer; 3 public-read var y: Integer; 4 public-read var width: Integer; 5 public-read var height: Integer; 6 7 public-init var javaRectangle: MyRectangle; 8 }
Code Sample 3: First draft of the adapter RectangleAdapter
Only thing left to do is to add the following line to the script in Listing 3. This will create a new instance of RectangleAdapter and its adaptee Rectangle.
1 import java.util.Observable; 2 3 public class MyRectangle extends Observable { 4 private int x, y; 5 private int width, height; 6 7 public int getX() { return x; } 8 public void setX(int x) { 9 this.x = x; 10 startNotification(); 11 } 12 13 public int getY() { return y; } 14 public void setY(int y) { 15 this.y = y; 16 startNotification(); 17 } 18 19 public int getWidth() { return width; } 20 public void setWidth(int width) { 21 this.width = width; 22 startNotification(); 23 } 24 25 public int getHeight() { return height; } 26 public void setHeight(int height) { 27 this.height = height; 28 startNotification(); 29 } 30 private void startNotification() { 31 setChanged(); 32 notifyObservers(); 33 } 34 35 // ... more functionality here 36 }
Code Sample 4: MyRectangle implementing Observable
To finish this solution, we need our adapter to extend the interface Observer. That requires to implement its method update(), and to register the adapter. The registration is done by calling addObserver() in the onReplace-trigger of the attribute javaRectangle.
The method update() comes with two parameters, which are helpful, if we want to observe more than one object or if we are dealing with different events. But in our code example, we can ignore them. Only thing left to do is pull the new values from javaRectangle and update the attributes of the adapter.
But wait! You have to be careful here. While our Java code can run on any thread, JavaFX Script is picky here. We need to make sure, that the update is done on the right thread, otherwise the bindings and subsequent actions might fail.
JavaFX Script provides a mechanism for this. The method FX.deferAction() takes a simple function as a parameter and makes sure, that it is executed on the right thread. The complete adapter is shown in code sample 5.
1 import java.lang.Object; 2 import java.util.Observable; 3 import java.util.Observer; 4 5 public class RectangleAdapter extends Observer { 6 public-read var x: Integer; 7 public-read var y: Integer; 8 public-read var width: Integer; 9 public-read var height: Integer; 10 11 public-init var javaRectangle: MyRectangle 12 on replace { javaRectangle.addObserver(this); } 13 14 override function update(observable: Observable, arg: Object) { 15 FX.deferAction( 16 function(): Void { 17 x = javaRectangle.getX(); 18 y = javaRectangle.getY(); 19 height = javaRectangle.getHeight(); 20 width = javaRectangle.getWidth(); 21 } 22 ); 23 } 24 }
Code Sample 5: RectangleAdapter implementing Observer
The second solution does not require any change to the Java class. Let's assume, we do not want to bind to an object of our own Rectangle, but to an instance of java.awt.Rectangle. This class does not provide any mechanisms to observe state-changes. Instead we have to pull the current state on a regular base to notice changes.
The JavaFX class PauseTransition provides exactly the needed functionality. It comes with two main attributes to setup the behavior. The parameter action defines the task, which needs to be executed, and the parameter duration defines the gap between two consecutive calls.
Defining the right value for the duration is probably the toughest part. The best choice depends on the context. If the value is too low, precious CPU cycles are wasted, if the value is too high, the update might come too late. In our example, the duration is set to 10 ms, which ensures smooth animations.
The adapter for the second solution is shown in code sample 6. Note, that we do not have deal with threading in this solution, because the action of the ActionTransition is already executed on the right thread.
1 import java.awt.Rectangle; 2 import javafx.animation.Timeline; 3 import javafx.animation.transition.PauseTransition; 4 5 public class RectangleAdapter { 6 public-read var x: Integer; 7 public-read var y: Integer; 8 public-read var width: Integer; 9 public-read var height: Integer; 10 11 var action: PauseTransition; 12 public-init var javaRectangle: Rectangle 13 on replace { 14 action = PauseTransition { 15 duration: 10ms 16 action: function(): Void { 17 x = javaRectangle.x; 18 y = javaRectangle.y; 19 height = javaRectangle.height; 20 width = javaRectangle.width; 21 } 22 repeatCount: Timeline.INDEFINITE; 23 } 24 action.play(); 25 } 26 27 public function stop() { 28 action.stop(); 29 } 30 }
Code Sample 6: RectangleAdapter pulling the status
This article presented two solutions to implement a simple binding of a Java object. The first solution is always preferable, because it uses CPU cycles only when an update took place, while the second solution burns cycles even when nothing happens. The second solution is a fall back in cases, where the observer pattern cannot be implemented.
It’s been done. Today the first version of the JavaFX SDK was released. What better occasion could there be to reactivate my blog? And it is a good time to stop for a moment and think about the past year in which I have worked on JavaFX.
I started to learn JavaFX (or was it still called F3 at that time?) a little more than a year ago, mostly in my spare time. I liked the ideas of the language and enjoyed playing with that. When my manager asked for volunteers to help the JavaFX compiler team a couple of weeks later, there was certainly nothing to stop me and so I joined the probably most professional and most experienced team, I have ever worked with.
The beginning was a lot of fun. The language had changed quite a lot compared to the interpreted version, which I was used to, but there was no documentation at all. So I had to learn JavaFX Script the hard way, by studying the grammar-file. Ever tried to learn a language by looking at the grammar? It’s definitely something one should try. π
The first weeks were probably the most exciting once. The syntax of the language changed almost every week, there were zillions of discussions on our mail-alias going on at the same time about different aspects of the language, and there were only very few of us, but all of us dedicated and devoted to this new thing called JavaFX.
Personally, it was amazing how much I was learning. For example, in the past, I always thought, it’s quite freaky, if C/C++ – developers were looking at the code, which was generated by the compiler. They should focus on the high-level language, right? That’s why it was invented, wasn’t it? And here I was: not only looking at the Java code, which was generated by the JavaFX compiler, but also at the bytecode, which was generated from Java code…
And then there was JavaOne. All of us had worked hard, especially during the weeks immediately prior to JavaOne, and we were excited to see how people would react. One needs to know, that JavaFX was still questioned by a lot of people at that time. More than once, I had conversations with my colleagues like this: “Is JavaFX already dead?” – “No, wait until JavaOne and you will see…” – “If it dies slowly or fast?” π
As we all know by now, JavaOne was a major success for JavaFX. We presented our prototypes on different devices, showed cool new features, and most importantly people became interested. And everything changed. Almost over night, JavaFX became one of the biggest efforts at Sun – and a prestigious one, too. Suddenly people from everywhere were joining the effort, mostly managers and architects though, not so many code-monkeys like myself. π All over the place new projects were started, processes and committees were established… For better or for worse, the effort grew tremendously right after JavaOne.
The last couple of months were dedicated mostly to finish our work for the 1.0 release: implementing the last missing pieces, fixing bugs, improving the performance… The beta release was pretty unspectacular IMO and we went back to our daily routine pretty quickly. But now the 1.0 version is out, and once again I am very excited to see, what people think about the product.
Oh, yes, and before I forget, here is the link, were you can download the JavaFX SDK and see a whole bunch of samples with source-code. Have fun and tell me what you think! π