JavaFX 8 is included in JDK 8 and is the officially recommended graphics toolkit for Java 8 applications. I’ve been using JavaFX 2 since 2012 and am very pleased as it was already a major step forward compared to Swing. Now with JavaFX 8 it has become even better, with new UI controls like DatePicker, a modern theme called Modena, rich text support, to just name a few.
Updated Tutorial and Blog Articles
I decided to rewrite the entire [tutorial series on JavaFX 2](/library/javafx-2-tutorial/) for [JavaFX 8](/library/javafx-tutorial/). There are quite a few things that are more comfortable now.
Updated Tutorial
Updated Articles
What’s New?
In the following I will describe the most significant changes I made. This should give you a fairly good overview of the new possibilities of Java 8 and JavaFX 8.
New JavaFX 8 UI Components
→ See JavaFX 8 Tutorial - Part 1
JavaFX 8 includes some new UI components:
TextFlow
- supports Rich TextTreeTableView
- a combination of TreeView and TableViewDatePicker
- yes, finally! Here is a short how-to article for the new DatePickerSwingNode
- for embedding Swing content into a JavaFX application- Various controls for 3D support
They are all supported in Scene Builder 2.
Adding Custom UI Components
I tried to add custom components in Scene Builder 1 but it didn’t work properly. It is now possible to permanently add a custom UI component in Scene Builder 2. It is bit difficult to find, though:
You should also be able to drag-and-drop a custom component into the library.
Various Scene Builder Improvements
There are various smaller improvements that were made. Overall, it’s a very pleasant experience to work with Scene Builder 2!
JavaFX Properties
→ See JavaFX 8 Tutorial - Part 2 (Create the Model Class)
JavaFX Properties were already available in JavaFX 2 but I chose not to use them to keep the model class simpler. Now, I think the advantages are enough to justify a little more code in the model class.
Properties provide means to listen for changes. This helps keep the view in sync with data changes in model classes. When working with JavaFX you will find Properties used all over the place. So it is important to understand a little bit about the their philosophy.
Let’s take a look at a small part of the Person
class in the tutorial:
public class Person { // Define a variable to store the property. private final StringProperty firstName; // The getter for the property's value. public String getFirstName() { return firstName.get(); } // The setter for the property's value. public void setFirstName(String firstName) { this.firstName.set(firstName); } // The getter for the property itself. public StringProperty firstNameProperty() { return firstName; } }
The new convention here is to use a wrapper class like StringProperty
instead of a simple String
value. By returning the StringProperty
with the firstNameProperty()
method other classes can get access to the property. The three methods should always be named exactly according to the convention.
With the properties in place we can add a listener as follows:
Person p = new Person(); p.firstNameProperty().addListener((observable, oldValue, newValue) -> { System.out.println(newValue); });
Another example: Listening to text changes of a TextBox.
Java 8 Date and Time API
→ See JavaFX 8 Tutorial - Part 2 (Create the Model Class)
In the Person
class we’re using the LocalDate
type for the birthday
. LocalDate
is part of the new Date and Time API. Finally(!), Java has a consistent and convenient way to work with dates and time!
Java 8 Lambda Expressions
→ See JavaFX 8 Tutorial - Part 2 (The PersonOverviewController)
→ See JavaFX 8 Tutorial - Part 3 (Listen for Table Selection Changes)
From the start, JavaFX 8 has been designed to support the new Java 8 language features. Especially Lambdas make the UI code much more pleasant.
As an example, let’s take a look at how to set a cellValueFactory
to a TableView
column. The cellValueFactory
is used to determine which field inside our Person
object should be used for a particular column.
CellValueFactory with and without Lambdas
// With Lambdas firstNameColumn.setCellValueFactory( cellData -> cellData.getValue().firstNameProperty()); // Without Lambdas firstNameColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person,String>, ObservableValue<String>>() { @Override public ObservableValue<String> call(CellDataFeatures<Person, String> cellData) { return cellData.getValue().firstNameProperty(); } });
Another option would be to use the PropertyValueFactory
(we used it in the JavaFX 2 version of the tutorial). For the PropertyValueFactory
we need to provide the property firstName
as a String. I prefer the versions above as this isn’t type-safe and thus error prone: When you change the name of the property you might forget to change the String of the PropertyValueFactory
.
// With PropertyValueFactory (not type-safe) firstNameColumn.setCellValueFactory( new PropertyValueFactory<Person, String>("firstName"));
ControlsFX Dialogs
→ See JavaFX 8 Dialogs
→ See JavaFX 8 Tutorial - Part 3 (Error Handling)
If you need a UI component that is not included in the standard JavaFX you can always take a look at the excellent ControlsFX project. I use it mostly for the great Dialogs.
Dialogs.create() .owner(stage) .title("Information Dialog") .masthead("Look, an Information Dialog") .message("I have a great message for you!") .showInformation();
Using JAXB instead of XStream
→ See JavaFX 8 Tutorial - Part 5 (Persisting Data as XML)
In the JavaFX 2 version of the tutorial we were using XStream for converting to and from XML. This was working ok for the simple model classes. Now that we’re using JavaFX Properties
in our Person
model, XStream causes some trouble. XStream tries to convert the entire Properties
to XML which generates a very large file.
With the JavaFX Properties
in the model we need to tell the XML converter to use the getters
and setters
and not the Property
fields. This can be done in JAXB with the @XmlAccessorType
. In fact, the default accessor type is PUBLIC_MEMBER
which means that our private Property
fields will be ignored.
Another reason to use JAXB is that it is already included in the JDK which means we don’t need to add another library.
LocalDate Adapter
→ See JavaFX 8 Tutorial - Part 5 (LocalDateAdapter.java)
JAXB doesn’t know how to convert the new LocalDate
type. We must provide a custom LocalDateAdapter
that tells JAXB how to marshal (convert LocalDate
to String
) and unmarshal (convert String
to LocalDate
).
Deplyoment
→ See JavaFX 8 Tutorial - Part 7
The e(fx)clipse plugin has been improved: We don’t need to worry about editing the eclipse.ini
file to ensure Eclipse is started with the correct JDK. That’s great and I hope this actually works on all platforms…
Deplyoment is always difficult. I’ve tried to write it as simple and clear as possible but I’m pretty sure there will still be some obstacles.
I hope the new tutorial and articles will help you create some fantastic apps. I’d love to hear how it’s working for you.
Marco