Everything you need to know to start using JavaFX

This might not be your first time dealing with user interfaces in Java, so chances are you already familiar with one of the many frameworks around. Swing, AWT, Java2D and so on, there are so many choices, but why so many? And what is the correct choice for your application?

I've pondered those questions myself and after dealing with both JavaFX, Swing and a bit of AWT, my advice, assuming you are running on Java 7 or higher, is to jump straight on the JavaFX bandwagon.
This doesn't mean there's anything bad with Swing or the other technologies, but I think they lack many of the requirements of modern UI development. For example, in Swing, the whole programming logic between the handling of the UI thread might not only be outdated, but also makes hard for developers to debug multithreaded applications.

Today Java FX is the new promising to be standard for developing rich desktop applications. Created directly by Oracle, it provides the right tools for building reactive and beautiful UI; on top of my head some of the features I like the most:

  • A good separation between template and control
  • A rich set of modern components
  • An easy to use scene builder
  • Good management of resources
  • Multi language application development support
  • Visual effects and support of CSS
  • Easy to create dialogs and other out of the box general UI practice

In the following article I'm going to introduce you to all the basics of JavaFX, providing examples in order to quickly grasp all the underlying concept of the framework while learning the basics to build awesome UIs in Java FX.

Basics

Java FX architecture diagram A quick glance at the underlying layers of Java FX.

The scene graph

If you have ever built an application with Swing you are probably already familiar with the concept of a scene graph. A scene graph is the entry point of our graphical application, an hierarchical tree structure that specifies with each node a visual element or a property to be added to the application's graphics.

The main advantage of Java FX, compared to Swing, is that you don't have to worry about the rendering process at all, being it completely encapsulated. You don't need to invoke any primitive drawing method directly, instead you focus on managing the UI logic with the use of the scene graph API. At any given time the system knows what objects to display, what areas of the screen need repainting, and how to render it all in the most efficient manner.

Example of a simple scene graph

A single element in a scene graph is called a node. Each node has an ID, style class, and bounding volume. With the exception of the root node, each node in a scene graph has a single parent and zero or more children.

Each node then defines the graphics (and control) of a specific area of the UI, extending one of the classes provided by the package javafx.scene of which three main classes are:

  • Node: The abstract base class for all scene graph nodes.
  • Parent: The abstract base class for all branch nodes. (This class directly extends Node).
  • Scene: The base container class for all content in the scene graph.

Some of the classes that inherit directly from the Parent class are Control, Group, Region, and WebView. The leaf node classes are defined throughout a number of additional packages, such as javafx.scene.shape and javafx.scene.text.

Entry Point

The underlying setup of Java Fx might be complex, but all it's logic is encapsulated inside the javafx.application.Application class that you have to extend.
All you need to do is to override the start method and assign a new Scene object to the Stage object you have as a parameter.

A JavaFX application defines the user interface container by means of a stage and a scene. The JavaFX Stage class is the top-level JavaFX container. The JavaFX Scene class is the container for all content.

Next let's see a simple code to show how we can access and handle the scene graph.

public class Main extends Application {

   @Override
   public void start(Stage stage) {
       Group root = new Group();
       Scene scene = new Scene(root, 500, 500, Color.BLACK);

       Rectangle r = new Rectangle(25,25,250,250);
       r.setFill(Color.BLUE);
       root.getChildren().add(r);

       stage.setTitle("JavaFX Scene Graph Demo");
       stage.setScene(scene);
       stage.show();
   }

   public static void main(String[] args) {
       launch(args);
   }
}

The important points to consider are as follows:

  • The root node (in this case, an instance of the javafx.scene.Group class) is created and passed to the scene's constructor, along with the scene's width, height, and fill.
  • The stage's title, scene, and visibility are all set.
  • The main method invokes the Application.launch() method.

result of the previous code

The resulting application appears as it does because black is the scene's fill color. Because the root node currently has no children, there is nothing else to display.

The previous example used simple shape objects to show the basics of how to handle the scene graph, of course a variety of other nodes can be used to achieve the most usual UI, we'll soon see that in the next section.

Creating layouts with FXML

FXML si an XML-based language that provides the structure for building a user interface separate from the application logic of your code.

I highly recommend building your scene using FXML layouts, adopting a good separation between the model, view and control in your application's code, mainly because:

  • Your application code will be much easier to read and maintain (separation of concern)
  • A designer can work on the layout part without tampering the code
  • You can make use of layout generation tools like JavaFx Scene Builder
  • You have tools at your disposition that can preview changes on the UI instantly

So if you want to develop a standard layout you have first to get familiar with some of the scene graph fundamental components.

Containers

Containers are nodes that specifies how their children should be positioned inside them. Below I'm gonna quickly describe some of the most useful ones.

Border Pane Provides five regions in which to place nodes: top, bottom, left, right, and center. If your application does not need one of the regions, you do not need to define it and no space is allocated for it.
HBox & VBox HBox Provides an easy way to arrange elements in horizontally , you can also specify the separation between the elements. VBox works in a similar way, but vertically.
Stack Pane In a Stack Pane each node will be added on top of the previous one, this layout model provides an easy way to overlay text on a shape or image or to overlap common shapes to create a complex shape.
Grid Pane The GridPane layout pane enables you to create a flexible grid of rows and columns in which to lay out nodes. Nodes can be placed in any cell in the grid and can span cells as needed. A grid pane is useful for creating forms or any layout that is organized in rows and columns
Flow Pane Provides a layout where you can easly add new nodes that will be automatically added after the previous one, row after row, from the left to the right (if the flow is set to be horizontal).
Anchor Pane The AnchorPane layout pane enables you to anchor nodes to the top, bottom, left side, right side, or center of the pane. As the window is resized, the nodes maintain their position relative to their anchor point.

Here you can have a more in-depth look at each builtin layout of Java Fx.

Controls

Java FX provides many control type nodes ready to be used, some famous ones:

  • Label
  • Button
  • Radio Button
  • Checkbox
  • Text Field
  • List View
  • Table View

For a more in depth look at each component, check out this nice Oracle tutorial.

FXML layout example

Creating layouts with FXML is very simple if you already used some xml based view model like the one used with Android app development you are probably already ready to go.

With FXML we are going to describe the structure of a specific layout that can be loaded by our application and converted in a scene graph, that we can access with the javafx API described earlier.

In the code below, let's create a simple layout containing an empty ListView and some controls.

<BorderPane xmlns:fx="http://javafx.com/fxml/1"  
            xmlns="http://javafx.com/javafx/2.2">
    <top>
        <VBox alignment="CENTER">
            <children>
                <Label text="Select a person from the list"/>
                <Separator/>
                <Button text="Clear selection"/>
            </children>
        </VBox>
    </top>
    <bottom>
        <ListView prefHeight="100"/>
    </bottom>
</BorderPane>  

The result should be something like the following:

A few thing you will surely notice:

  • BorderPane is the root node of this layout
  • We used the property alignment of the top placed VBox to nicely center its children
  • The parts right and left of the BorderPane are not specified, so they are ignored

Control-View binding

Now that you have successfully created your fist layout, it's good to know how you can access all its components by means of a Java class, in order to add behavior to you Application's UI.

Let's start by creating a clean Java class called Controller. This class will be responsable of handling the setup and behavior of the previously created FXML layout. To bind this controller to the layout just add the attribute fx:controller="sample.Controller" to the BorderPane node, being it the root one. Note that sample.Controller is the full path of the class in package form.

We can also mark a specific node with an id using the attribute fx:id in order to quickly get its reference in the controller by injection.
Marking the button and the ListView, we should have something like the following:

<BorderPane xmlns:fx="http://javafx.com/fxml/1"  
            xmlns="http://javafx.com/javafx/2.2"
            fx:controller="sample.Controller">
    <top>
        <VBox alignment="CENTER">
            <children>
                <Label text="Select a person from the list"/>
                <Separator/>
                <Button text="Clear selection" fx:id="clearBtn"/>
            </children>
        </VBox>
    </top>
    <bottom>
        <ListView prefHeight="100" fx:id="peopleListView"/>
    </bottom>
</BorderPane>  

Nodes having the attribute fx:id will have their reference injected inside the Controller.java class, binding the variables with the name same as the fx:id, as long as they are public or marked with the @FXML annotation.

package sample;  
public class Controller {  
    @FXML
    private ListView peopleListView;
    //public fields doesn't need the @FXML annotation
    public Button clearBtn;
}

Create a Scene from an FXML layout

Now that you have created your FXML layout you can easily generate the corresponding structure in Java with a few calls.

Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));  
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();

You can guess what happens in this piece of code. FXMLLoader.load returns the root node of your FXML structure (being it BorderPane, but we want to stay flexible, so the runtime type doesn't matter), given the reference to the sample.fxml resource file (that I've put in my project root folder).

After that the code assigns the returned scene graph to the primary stage and opens the window system. Of course you can do anything else as you please, like changing the content of a stage dynamically.

Scene Builder

Scene builder is an amazing tool that renders and updates FXML files on the fly. It is great for newcomers to understand in a practical way all the different properties of the scene graph and experiment with styling.

You can get this tool directly from the Oracle webpage here.

For a direct example and tutorial of the Scene Builder capabilities I advise you to follow this video from the Java Youtube Channel.

Looks like this amazing tool will be discontinued soon, but fear not.

If you are using IntelliJ IDEA, one of my advice is to download the standalone application and forget about the plugin, since it doesn't provide a fluid experience.

Deployment with IntelliJ IDEA

If you are using an IDE like IntelliJ, this part comes quite easy, since the tool is already equipped with all the functionalities you need, guiding you step by step through the process.

There are different options here you might want to consider before deploying your application:

  • you can create a simple .jar executable file
    • considering the fact that you want to include or exclude external libraries
  • you can create a cross-platform installer

Creating a self-contained application package will definitely make things easier if you are going cross-platform. In the past, the user experience for installing a Java application hasn't always been smooth, different java versions, different access rights, user-settings, etc made the process not quite straight forward for the end user.

Fortunately, JavaFX provides a new deployment option called Native Packaging, a bundle containing both your application code and the (platform-specific) Java Runtime.

Lets quickly see what to do in order to build a javafx application with IntelliJ IDEA.

  1. You have already created a javafx application project, if not create one and move you code.
  2. Go to File > Project Structure > Artifacts

At this point the process is quite straight forward.
Use the plus sign icon to create a new Artifact, either a JAR or a Java FX application. On the right you can specify all the project contents and all necessary dependencies, so that in the /out folder you will find all your deployed files.

Note that in order to include all your libraries inside the jar you need to select the JAR option from the up left + sign. A prompt will ask for your main class, after that select EXTRACT TO TARGET JAR.

For more details about building with intellij go here.

References

comments powered by Disqus