CSC1120: A JavaFX Overview

This reading is an overview of the structure of JavaFX. It is designed to give you a deeper understanding of how JavaFX works and what is actually happening under the hood when you use the JavaFX libraries.

Contents

Installation Instructions

Introduction

JavaFX was first released in 2008 as a "modern" reworking of a Graphical User Interface (GUI) for Java. Previously, Java had used the Abstract Window Toolkit (AWT) and later Swing, a toolkit based on AWT. Why does this matter? Because there is an awful lot of AWT and Swing code still in use, and many AWT and Swing classes that have the same name as newer JavaFX classes. As a result, when you are building a JavaFX GUI program, you need to make sure you do not import AWT, javax, or Swing components, as the two systems are completely incompatible.

Now, why did Java completely redesign their GUI toolkit? The answer is flexibility. JavaFX can be used in a variety of settings, including mobile apps and web apps, alongside desktop applications. The ability to style the GUI using the same tools as a webpage (CSS) and easily switch between different views (or Scenes) within the same window make it more usable for modern programmers. Additionally, JavaFX has the ability to use a markup language (FXML) similar to HTML to make it's development even more streamlined. We will cover FXML in the next chapter.

These days, JavaFX is developed separately from the main Java language, requiring a bit more effort to include the JavaFX libraries in a new project. This was done because Java has drastically increased the speed at which new versions of the language are being released, and JavaFX development did not align with the features and schedule of the main language. It is still in active development and has many new features and tools available.

JavaFX As a Theater

One of the biggest improvements that JavaFX brought initially over Swing is it's organization. It is built as a hierarchical structure that contains many components that may themselves contain more components. Let's imagine that building a JavaFX application is like staging a play at a theater.

  1. The Stage: In the theater, the stage is where all the action happens. In JavaFX, the Stage class represents the main window of your application. Just as you can control the size and layout of a physical stage in a theater, you can control the size, title, and other properties of your JavaFX Stage. This is generally thought of as the main window of the application, though as you will see below, that is only mostly correct.

  2. Scenes: Theatrical plays can have multiple scenes. Each scene provides a different setting and context for the actors. Similarly, in JavaFX, the Scene class represents the visual content of a stage. It contains all the elements or actors (which we'll get to next) for a specific segment of your application. Think of each JavaFX Scene as a different setting in your play. In a mobile application, you have only one small screen to work with, and will need various Scenes to display the different views of your program. For example, you may have a login screen, a main program screen, a settings screen, and any number of subsections of these main screens. Each one would be defined by their own Scene. They can be thought of as the stage plot, or a blueprint of where things should be locate on the Stage

  3. Actors (Widgets): In a play, actors perform on the stage. In JavaFX, your actors are UI components or widgets like buttons, text fields, and sliders. These are instances of various JavaFX classes such as Button, TextField, etc. They perform specific roles in your application, allowing users to interact with the content.

  4. Costumes and Makeup (CSS): Actors often wear costumes and makeup to appear in a certain way. In JavaFX, you can "dress up" your UI components using Cascading Style Sheets (CSS). This lets you change their appearance, like colors, sizes, and styles.

  5. Scripts: Every actor in a play has a script, a predefined set of actions and dialogues. Similarly, in JavaFX, you attach event listeners to your UI components. These event listeners determine how the "actor" or component should react when a user interacts with it, such as clicking a button.

  6. Director (Main Application Logic): Behind every play, there's a director guiding the actors and ensuring everything runs smoothly. In your JavaFX application, the main application logic (typically inside your start method) acts as the director. It sets the stage, decides which scene is currently showing, and oversees the interactions of the actors.

  7. Props (Data Models): Many plays use props, physical items that actors interact with to support the narrative. In JavaFX, you might have data models or objects that represent the information you're displaying or manipulating. They aren't visual components, but they're vital parts of the story your application is telling. The data model should be contained in a separate Java class from the JavaFX GUI components.

  8. Backstage Crew (Background Processes): Behind the scenes in a theater, there's often a crew managing lights, sounds, and set changes. In JavaFX, background tasks and threads are like this crew. They perform operations without directly appearing on the main stage (or UI thread), ensuring the application runs smoothly.

So, to sum it up: building a JavaFX application is like staging a play. You have a main window (Stage), different settings (Scenes), actors (UI components) that users interact with, costumes (CSS) to style those actors, scripts (event listeners) that define actions, a director (main application logic) to guide everything, props (data models) to support the narrative, and a backstage crew (background processes) to manage behind-the-scenes tasks.

The Scene Graph

Okay, so we have this wonderful theater metaphor to help us visualize construction of our GUI, but how does it all actually work? This is, after all, just Java code and not a real theater production. The answer lies in the Screen Graph. The Screen Graph is often described online and in documentation in slightly complicated algorithmic language, but at it's core it is simply a hierarchical structure like a family tree. Just as a family tree shows relationships between family members – parents, children, and siblings – the scene graph shows the structure and relationship between visual elements. The Scene Graph is stored in memory and updated periodically to reflect changes to the nodes when the GUI is running.

How it is organized:

  1. Root Node: At the very top of this family tree, you have a "Root." This is the main container or parent for everything you want to show on the screen. In code this is usually stored in a Parent object, which is the superclass of all Pane classes. Each different Pane class, such as a FlowPane or a GridPane, organizes it's Scene in a specific way.

  2. Children Nodes: Under this root, you have its children. These children can be basic shapes (like circles or rectangles), text, images, buttons, or other interface elements. Just like in a family tree, where children can grow up and have their own children, these visual elements can also contain other elements within them.

  3. Branches & Leaves: Nodes that contain other nodes are often called "branch nodes" (like parents and grandparents in a family tree), while nodes that don't contain others (they're at the end of a chain) are called "leaf nodes" (like the youngest members of the family).

  4. Order Matters: In this family tree, the order in which you add children matters. Think of it like stacking books on a table. The first book you put down will be at the bottom, and every new book you place will be on top of the previous ones. Similarly, the first visual element (or node) you add will be drawn first, and subsequent elements will be drawn on top.

  5. Transformations & Properties: Just as each person in a family tree might have unique characteristics (like height, eye color, hobbies), each node in the scene graph has properties, such as position, color, and size. Plus, you can apply transformations to these nodes, like rotating them, scaling them up or down, or moving them around. An example would be a Button will have a number of properties, such as the text on the Button, it's color and shape, it's location, what happens when it is clicked, and so on.

How it is used

The practical application of the Scene Graph is that every Stage will have one active Scene. That Scene will have a Parent container that is the root of the graph, or the first ancestor of the family tree. From there, we add the components we need, such as Buttons, TextFields, etc. in the order the will be drawn. This means the last component added will be the last component drawn. Finally, we add the listeners and event handlers to the components and whatever styling (color, location, shape, etc) we want to the individual components.

Rendering and Updating

All of this will draw the initial objects onto the Stage. But how do the components know when to run their event handlers? When does the GUI update when new information is added? There are several layers of code that are required to draw the GUI to the screen, but the layer that exists between the code you write and the hardware is called the Quantum Toolkit. You won't need to know how it works, necessarily, but it is important to know it exists and what it does. It is software that runs concurrently with your program and is responsible for synchronizing the Scene Graph with the actions taken by the GUI and the rendering (drawing) of the components.

When a component is interacted with, whether it is a Button being clicked on or typing text into a TextField or something similar, that object has a private instance variable set to mark that component as needing attention. The Quantum Toolkit has a pulse, which is usually aligned with the system display's refresh rate, or as close as it can get. For example, for a system that has a monitor with a 60 Hz refresh rate, the Quantum Toolkit would attempt to check on the Scene Graph looking for changes 60 times a second. When it detects that a component requires attention, the appropriate event handler methods are run, the changes in memory are updated, and the toolkit pushes the updates to the lower software layer, called Prism, which does the calculations to determine what components to redraw and where to redraw them. The components are then redrawn in the window.

The Application class

Making all of the above happen is the JavaFX Application class. The Application class is an abstract class that handles the launching and closing of a JavaFX GUI application and must be extended by the program's driver class. Application has several necessary methods for a JavaFX program, including the static launch() and the abstract start() methods. When a JavaFX program is started, it begins like any regular Java program: with the driver class' main(String args) method. From there, a number of separate things occur to fully launch the JavaFX application.

  1. launch(String[] args): The first step in starting a JavaFX application is calling the launch() method from the driver class' main() method. The args from the call to main() are passed into the launch() method, which does the following
    • Initializaes a new thread for the JavaFX runtime. This is essentially setting up how the GUI portion of the program will run.
    • Once the initialization is complete, launch() instantiates the main class that is extending Application.
    • It then calls the unfortunately named helper class, LauncherImpl, which verifies everything needed to launch the application is correctly initialized. It then initializes the default starting Stage object and sends it to the instance of the main class.
  2. init(): After launch() completes, the JavaFX runtime calls the init() method from the Application class. This is where any custom setup or initialization for the program would be written. The default init() in Application is empty, so you would need to override the parent implementation with your own, if needed.
    • Note, you do NOT manually call init() in your code. It is always called by JavaFX before the program begins.
  3. start(Stage primaryStage): After init() has been called, JavaFX then calls the concrete implementation of the start() method, passing in the Stage object created earlier. This implementation is the point at which the Scene will be built and added to the Stage. The final line of code in the start() method should be to show the Stage