Motivation / Goal
Just looking at JavaFX code doesn't get you too far in terms of understanding it. Let's create a little problem for us to solve in JavaFX. Going through the steps one-by-one is really the only way to learn.
Something simple. How about a calculator? A text field, some buttons with actions, we're all good. Oh, wait, I'm way to lazy for that. Let's scale it back to something like a calculator. How about, a frame with a text field, which is not editble. There will be buttons to add characters to the text field, or clear it out. Upon every textfield change, let's print the old and new text value.
Pretty simple, pretty useless. But it should hit upon several JavaFX features. Afterwards, we will make an equivalent swing version.
Building the JavaFX application
Let's make an attempt at the JavaFX version with SDK 1.0.
You will want to at least follow the Getting Started tutorial over on OpenJFX to the point of getting your IDE set up. Eclipse set-up is largely the same as Netbeans, so the instructions there still apply.
Hello World
We'll start with the Hello World example on that page:
Code | Preview |
---|---|
|
Adding a text field
So, there are a good amount of changes needed to reach our goal. We'll need a Swing Component Textfield, one that is not editible:
Notice that I have introduced a a Swing Component TextField where I made the editable property false.
Multiple Components
We need to add buttons, so we will have multiple components in this frame. Traditional GUI programming would have us set up layout managers to accomplish this. For this simplistic example, we can use Swing Components, SwingButton, SwingTextField.
In JavaFX SDK 1.0, there are some convenience classes that help us with this:layout, in the javafx.scene package. In layout, HBox - create a horizontal box container and VBox - Vertical Box container and as the names suggest content i.e. buttons are either aligned horizonality or vertically according to the selected choice.
We simply use an array of components, denoted by the '[' and ']' symbols. Each component is seperated by commas, as in Java. Note that, also like Java, the final component can have a trailing comma, which is ignored (see tag note2).
Adding Behavior
We want something to happen in response to button clicks, namely that they change the value of the textfield. To get to that point, we need to add the equivalent of ActionListeners to our buttons.
From this point on, the preview of the GUI stays the same, so I will not be including them. Also, you must have the Java Console visible, or be running JavaFX from the command line in order to see the System.out.println results.
Code |
---|
|
Adding an ActionListener is pretty straightforward. Define an function() that does what you want. However, what we want is to change the text in the textfield, and we can't quite get there just yet.
Also note the import statement tagged with note1... almost every class must be imported explicitly. There is no freebie import java.lang.* as there is when coding Java.
Binding a data model to a view
(For a more detailed tutorial on binding, see Introduction to Binding in JavaFX.)
In JavaFX SDK 1.0, a developer is free to define new classes and assign them to variables. In the same way variables can be defined to replace any code that is used in a javaFX script ("var txt = Text {...} or var myButton = SwingButton {..}" and then call them appropriately. This provides great flexibility in how we organize our code, and can help in separating out non-visual behavior and clarifying the code.
There is something else, something very cool, that building our own data models provides us: binding. We can make the value of some attributes dependent on others. For example, we can make the textField's text attribute dependent upon some other value, one that is easier for our buttons to access.
Code |
---|
|
There are three new entries at the top of the file: A class definition, a method definition, and a variable declaration. The class definition defines the values present on an object (attributes) and the methods (functions). The first strange thing you'll notice (lines tagged note1) is that the operation is not actually defined within the class definition. This was an intentional feature... see some of Chris Oliver's early postings to the mailing lists (TODO add link) as to why.
The variable declaration (tagged note2) is creating a new instance of TextValue and assigning its value attribute to "Hello, Model".
Our TextField's value attribute (tagged note3 is now a little different. Rather than specifiying its initial value as we declare the text field, we are telling it to use the value of model.value. Furthermore, since we are using the bind keyword, we are telling it to update every time that model.value updates. Also note that the binding is two-way; if the text field were editable, every change made to it would be reflected in model.value.
At the lines tagged note4 we have made the a and b buttons change the value of the model by concatenating 'a' or 'b' to the end of the string. Note also that we don't have the convenience of using the '+' operator... it is not overloaded for string usage.
For the Clear button's behavior (at note5) we could have done something similar to the other buttons, by assigning model.value = "". We used the operation clear() here just to show its use.
With note6, I have tried to remove all the content code and replaced only with a variable win and shown the declaration above the main staging area declared with the var win = HBox {..} Its the exact copy of whats in the HBox {..} in previous code did this is a good technique to extract and avoid coding errors or reducing complexity.
Adding Triggers
We are missing one thing from our original specification: Every time the text field changes, we want to print its contents to standard out. There is no syntax for adding a little code to the bind instruction; we can't just insert a sysout there.
So, we can move on to triggers, a bind-like feature of JavaFX. It allows us to do something when an attribute changes. Triggers are more sophisticated than shown here, this is only a simple usage.
Code |
---|
|
The only change here is at class TextValue at note 1. The syntax for triggers is interesting and has changed in javaFX SDK 1.0 but its interesting in implementation. Unlike before triggers don't have constructors and they apply simple SQL-like syntax ie insert, replace, delete operations to handle data modification events. Please review documentation for indepth information on triggers in javaFX.
In our case the following changes were made;
note 1a: declared a new var oldValue as a String
note 1b: appended to the var value trigger keywords "on replace {.doSomething..}"
note 1c: assigned value to the oldValue
note 1d: added clear the var oldValue just incase
Final and Annotated version
Here is the final version of the fx file, along with lots of comments inline.
Code |
---|
|
The Swing Version
For completeness, then, let's go back and make a Java Swing version just to see what it looks like compared to the JavaFX version. I'll skip the intermediate steps and present the final Swing version here. This is a quick-and-dirty implementation; I would never recommend coding Swing this way...
Code |
---|
|
While this code is short, it is largely uncommented, and I've cheated a couple of ways to get binding and the trigger working.
Conclusions
JavaFX offers a lot in terms of data binding and component setup. Developers wanting to really use the framework should be sure to write scripts that can take advantage of them.