Today, many graphical user interfaces (GUIs) live in a browser or a
mobile device.
However,
- There is a need for user interfaces in embedded systems such
as information kiosks and in-car displays,
- Java now runs on ARM processors, which are found in 60% of the
world's mobile devices, and
- There still is a demand for GUI applications for desktops
- There are frameworks for writing, compiling, and packaging
JavaFX applications for deployment to iOS and Android.
- Learning JavaFX makes it easier to learn other GUI frameworks such as
those used by Android (for mobile apps) and browsers (for web apps).
Early Java motivation
• Serve up "fat clients" over the nascent Web
AWT (Abstract Window Toolkit)
• Use native components from
various operating systems, but subtle differences caused major
problems
Swing
• Don't use native components; paint its own.
Initially, it was slow.
Flash
• Increasingly used for fancy effects
JavaFX 1.0
• Competitor to Flash; ran on the JVM but
required a new scripting language
JavaFX 2.0
• Has a Java API and no need for a separate
language
Java 8
• Distributed with JavaFX
Java 9+
• Distributed without JavaFX, which must be added
A JavaFX program's life-cycle is determined by an Application,
which contains a Stage on which various Scenes play out.
The
Application class (in package
javafx.application) is
the entry point for JavaFX applications.
It is abstract and must be extended by the programmer:
- The abstract start method must be overridden
- The init and stop methods may be overridden
The static
Application.launch method initiates the following
life cycle:
- An instance of the extended Application class is
constructed
- init is called
- start is called on a newly constructed Stage
object (discussed later)
- When the application is finished, stop is called
Here is the skeletal form of a JavaFX application:
The
Stage class (in package
javafx.stage) is the
top-level JavaFX container.
The primary
Stage object is constructed by the JVM for passing
to the application's
start method.
The
Stage is given a title and a scene, and then it can be
shown:
The
Scene class (in package
javafx.scene) is the
container for GUI content.
All content is represented as a hierarchical scene graph (or tree)
of
nodes.
Each node is either a:
- Branch node: it can have children, or
- Leaf node: it cannot have children
The
Scene constructor is given the scene graph's root node.
Below is a complete example. It creates a simple one-node graph whose
root is a
Label.
Label is an example of a user interface
control,
discussed next.
When this class is run, it produces:
Another example is the
DummyConsole class, which is a model for
all of the problem solving domains.
A JavaFX
Label is an example of a
Control:
- Controls are Nodes in the scene graph, and can be freely
mixed with images, media, text, and shapes
- Controls are Parents, specifically Regions, so they
can layout children and be styled with CSS (Cascading Style
Sheets)
Like other GUI component frameworks, JavaFX uses the MVC
(model-view-controller) architecture:
- Model:
- Contains the state of the control (e.g. a label's
text), and
- The operations that manipulate the state
- View:
- Computes minimum, maximum, and preferred sizes
- Responsible for containment testing
- Controller:
- Interacts with user
- Responds to GUI events
This section shows a few examples of how JavaFX controls can look.
Later we give examples of how to implement them.
Graphical user interface events include button clicks, slider
movements, etc.
Responses to events are given by instances of the following interfaces,
for example:
- EventHandler: for buttons
- InvalidationListener: for "observable" controls (sliders,
menus, etc)
Note: There is also a
ChangeListener interface for "observable
values" of controls, but:
- InvalidationListener is easier to use, and
- The difference between the two has to do with whether a property
is being computed from others (see next section), and is usually
immaterial.
EventHandler is a functional interface with
method
handle.
handle accepts an event object and responds to button clicks.
EventHandlers are given to buttons through the
Button class's
setOnAction method.
For many JavaFX controls, events are triggered when a
property
of the control changes. (The next section discusses properties in more detail.)
For example, for a slider:
- A slider has a property called value that emits
events when it changes.
- So, to respond to the user moving a slider, you listen for the slider's
value property to change.
- The Slider class's value property implements
the Observable interface (from the javafx.beans package).
- The Observable interface has an addListener method
that takes an InvalidationListener as an argument.
- InvalidationListener is a functional interface
whose invalidated method accepts a
property object and responds to the property changing.
A straightforward approach is to explicitly write a class that
implements
EventHandler, create an object of that class, and
pass it to the
setOnAction method:
This approach often results in writing classes that are instantiated
only once.
If a class is used only once, it can be defined using the
anonymous classing syntax:
This approach still requires that the class be defined to explicitly
override the
handle method.
Recall that a lambda expression can be used whenever an object of a
class implementing a functional interface (interface of one abstract
method) is expected.
Since
EventHandler is a functional interface, we can specify the
action of a handler in a lambda expression simply by specifying the
parameter of the
handle method (
event in this case) and
the code that
would have made
up the method's body in a class definition:
The code below makes use of a
VBox (vertical box, discussed
later) to layout a button and label.
Before clicking the button:
After:
Suppose we have a
Slider object called
slider and
a
Label object called
message whose text is
initially large:
After moving the slider:
We describe three ways of adding an
InvalidationListener to a slider.
A straightforward approach is to explicitly write a class that
implements
InvalidationListener, create an object of that class, and
pass it to the
addListener method:
This approach often results in writing classes that are instantiated
only once.
If a class is used only once, it can be defined using the
anonymous classing syntax:
This approach still requires that the class be defined to explicitly
override the
invalidated method.
Recall that a lambda expression can be used whenever an object of a
class implementing a functional interface (interface of one abstract
method) is expected.
Since
InvalidationListener is a functional interface, we can specify the
action of a listener in a lambda expression simply by specifying the
parameter of the
invalidated method (
observable in this case) and
the code that
would have made
up the method's body in a class definition:
Before moving the slider:
After:
A
property is an attribute of a class that you can read or
write.
In a plain, old-fashioned Java object (POJO), a property is inferred
from a getter/setter pair. In the
following we see the definition of a read/write property called
"
text" and a typical usage:
When running this class, we get:
Hello There
New Greeting
In order to add listeners to properties, JavaFX adds a third concept to
the getter and setter: a
property object.
For example, the
StringProperty class:
- Wraps a simple string
- Has methods for getting and setting the wrapped value
- Has methods for managing listeners
In the example below, a
SimpleStringProperty is created.
Property objects have
get and
set methods that work like
their POJO counterparts.
However, they also have an
addListener method to add code that
is called when a property changes, as shown below in the
main
method.
Note the similarity to the way a listener was added to the slider's
value property.
When running this class, we get:
Greeting set to Hello There
Greeting set to New Greeting
One of the most important features of JavaFX is the ability to
automatically update one property when another one changes.
All properties have a
bind method for this purpose.
The simplest form is:
Here
property1 gets
property2's value
whenever
property2 changes.
If you also want
property2 to depend on
property1 in the
same way:
Often
property1's value needs to be
computed
from
property2 when
property2 changes:
How this is done is explained below.
In this example, a second string property
another is bound
to
text.
As a result, when
text's value changes,
another's does as
well.
When running this class, we get:
before
after
In this example, when the user edits the top text area, the bottom one
is updated as well.
Code | Result |
|
|
The code below places a red circle inside a
Pane container.
Note:
- Circle extends the abstract Shape class, which is
also a Node. Shapes can thus be elements of scene
graphs.
- Pane is a basic container class for laying out child
components.
Code | Result |
|
|
As shown below, if the pane is resized, the circle maintains its
position:
Desired: reposition the circle to the middle of the pane as the scene
containing the pane is resized:
A
Circle has properties
centerX and
centerY for
the coordinates of its center in pixels.
A
Scene has properties
width and
height in pixels.
As a scene is resized, its width and height can be used to re-center
the circle:
- centerX = width/2
- centerY = height/2
Unfortunately, the following will not work:
because the argument for
bind must be a property.
The
Bindings class has many static methods for the purpose of
creating properties that are computed from others.
For our problem we would use the
Bindings.divide method.
Code | Result After Resizing |
|
|
JavaFX supports these approaches to layout management:
- Using a design tool:
JavaFX Scene Builder
- Under program control, using standard library calls
- Using XML (Extensible Markup Language)
- Using CSS (Cascading Style Sheets)
We will discuss the last three approaches.
Programmatic layout in JavaFX uses
panes, or containers with
fixed layout policy.
The JavaFX layout package is
javafx.scene.layout.
The
Pane class is the base class for layout management:
- Does minimal layout
- Defines a getChildren method to access its contained
elements
- Has several subclasses for performing more specialized layout
We have seen one,
VBox, that lays out its children in a single
vertical column.
This section presents layout code examples with snapshots of the scenes
they produce.
The
BorderPane class arranges children around the border or
center of the pane:
Code | Result |
|
|
The
BorderPane class has a static
setAlignment method for
horizontally aligning children:
Code | Result |
|
|
JavaFX incorporates the
Composite design pattern
that allows containers to both aggregate components and be contained in
other containers.
Thus, many layouts can be achieved through combinations of
VBox
(vertical box) and
HBox (horizontal box) layouts.
For example, here is the scene graph representing a label placed above
three buttons arranged in a row:
Below is code that creates the afore-mentioned scene graph along with
its result.
Note that by default no gaps are inserted between components.
Code | Result |
|
|
To produce a more pleasing layout, there should be
gaps between
the scene's elements, and
padding between elements and the edge
of the scene.
Below is the same code as before, with code added to produce the gaps
and padding. Note:
- The measurement unit used is a "root em" to
avoid the problem that pixel densities can vary across display
devices
- Gaps are given to the HBox and VBox constructors,
while
- Padding is a property of Region, a superclass
of HBox and VBox
Gaps and padding are part of a scene's
style. Later we show how
Cascading Style Sheets (CSS) can be used to style JavaFX scenes without
having to write Java code.
Code | Result |
|
|
A JavaFX
GridPane is like an HTML
table:
- Cells can span rows or columns
- Cells can be given horizontal and vertical alignments
Below is a scene laid out with
GridPane, along with a table
showing the elements used and their row/column coordinates.
- The text labels are horizontally aligned to the right in their
cells
- The third row has one element, an HBox, that spans two
columns
When you add to a
GridPane, you specify a column and row index
(in that order).
Scene | Elements |
|
|
The code below produces the afore-mentioned scene. Note:
- Alignment of the GridPane cells is handled with a static
method (setHalignment), as with BorderPane
- Alignment of the buttons is handled by a
non-static method (setAlignment) in the HBox
class
Seeing borders between nodes in a scene can be helpful when debugging
or trying to improve the scene's appearance.
Here are two ways you can display lines (temporarily), followed by an
example of their use:
- Gridpane has a gridLinesVisible property that controls
whether lines are displayed to show the cell boundaries.
- You can see the borders of containers within a cell, for example,
an HBox, using CSS (discussed later).
Code | Result |
|
|
This section describes how to use JavaFX to layout a GUI for the Voice
Mail System Simulator (VMSS).
We will assume the system's handset is of the standard land-line variety, and
not a smart phone with a touch screen.
We define a
TelephoneFX class that implements
the
Telephone interface and extends the JavaFX
VBox
class:
We can arrange the components of a handset vertically,
with the speaker at the top, followed by the key pad in the middle,
and the microphone at the bottom.
The first version of the
TelephoneFX class implements the
Telephone
interface and extends the
VBox class
in the
javafx.scene.layout package, but as yet does not add
any components.
Here is how the GUI renders, using default dimensions:
The
MailSystemTesterFXGUI class launches the application.
Note that a
TelephoneFX object is created, in place of
a
TelephoneNoGUI object. The rest of the launch code is the same.
The
MailSystemTesterFXMLGUI class launches the application.
Note that a
TelephoneFXML object is created, in place of
a
TelephoneFX object. With one exception — creating
the
Scene from
TelephoneFXML
— the rest of the launch code is the same.
The speaker pane consists of the label "Speaker:" above a non-editable
text area used to display a string simulating audio.
- So the speaker pane will also be a VBox, the first child of the
surrounding top-level VBox.
- Note that the size of the text area is computed from the dimensions of
the (not yet added) keypad buttons, so that changing their size will
automatically change the size of the text area.
Here is how the GUI renders:
The keypad buttons are naturally arranged in a
GridPane.
- We use a nested loop to manage row and column indices for the GridPane,
and we get the button label from a literal string.
- Note the use of the "DejaVu Serif" font family and the large
bold font style for the button labels.
- We also add a listener to each keypad button, which is simply to
send the button's label to the Connection object via
its dial method.
Here is how the GUI renders:
The microphone pane consists of the label "Microphone:" above an editable
text area used to input simulated speech, which is above a horizontal
row of buttons for sending the speech and hanging up.
So the microphone pane will be a
VBox, but its bottom
child will be an
HBox.
Here is how the GUI renders.
Here is the scene graph for the completed GUI:
The final version of
TelephoneFX is here:
TelephoneFX.java.
To run this version in NetBeans, simply add this class to
the
mailgui package and run the modified version
of
MailSystemTester.
This section contains the files necessary to create the console-based
version of the VMSS.
To run the simulation in NetBeans:
- Create a new Java application project
- In the project, create a new source package
called mailgui
-
Add each of these files to the mailgui package:
- Create a subpackage of mailgui called test
- Add this file to mailgui.test:
MailSystemTesterNoGUI.java
- Run the MailSystemTesterNoGUI file
It is possible to create GUI layouts in JavaFX using
XML, the
Extensible Markup Language, which we briefly describe here.
JavaFX uses an extension of XML called
FXML.
For more documentation on XML, see the World Wide Web Consortium's
XML
Essentials.
- A text-based format for representing structured information,
including both documents and data.
- Related to HTML, which we will cover later.
- Used to share information between programs, between programs and
people, across both local and global networks.
An XML document is a hierarchically organized group of
elements.
An element is specified using the following form:
where the
body of the element (the " ... ") can contain text
and/or other elements. Example:
Since XML document elements are nested, their relationships can be
depicted using a tree, as shown below.
Note the similarity in structure between the tree and a JavaFX scene
graph.
XML Document | Element Structure |
|
|
Elements can be given
attributes, using the following syntax:
Attributes can be thought of as adding to the element tree as shown
below.
XML Document | Element Structure |
|
|
Generally, FXML elements correspond to:
- JavaFX scene classes (GridPane, Button, etc.),
and
- Properties of JavaFX scene nodes
(children, padding, etc.)
Properties with simple values can be indicated using element
attributes.
Below is the presentation of our
GridPane example from before
followed by the FXML code that renders it:
Once you have created an FXML document two things must happen:
- The document must be parsed into a JavaFX object hierarchy
- The object hierarchy must be loaded into the Java virtual machine
This is accomplished with an
FXMLLoader.
For our example, suppose the document is named "dialog.fxml". Then the
following will produce the rendered scene:
The object hierarchy can be used to add listeners to GUI elements, as
shown in the Voice Mail System example.
This section describes how to use FXML to layout a GUI for the Voice
Mail System Simulator (VMSS).
We define a
TelephoneFXML class that implements
the
Telephone interface and loads XML elements from the
file
Telephone.fxml:
As with the programmatic layout, we will layout the telephone
components in a VBox.
- The first version of Telephone.fxml contains a
single VBox element that will serve as a top-level
container
- TelephoneFXML.java creates the object hierarchy and
eventually will add behavior to the GUI elements
Here is how the GUI renders, using default dimensions:
The speaker area is a nested VBox containing a label and a text area.
- Attributes are used to set component properties
- An id attribute for the text area is used so that it can
be referenced when the speaker "speaks."
Here is how it renders:
The microphone area is represented by a nested VBox containing a label,
a text area, and an HBox containing two related buttons.
- The text area, representing simulated speech, is given
an id attribute so that its text can be manipuated under
program control
- The buttons are given id attributes so that they can be
given listeners
Although the GUI will now render completely, it will not respond to
interactions until we add behavior.
Here is how it renders:
The keypad buttons need to be nested within a
GridPane element.
- Grid pane row and column indices are given explicitly
- An id attribute for the grid pane is used so that its
children (the buttons) can be given listeners under program
control
- Button style attributes are repetitive and insufficient (no font
information is given). This will be remedied with CSS.
Here is how it renders:
FXML elements can give form to a GUI, but not behavior. To add behavior,
we must find the JavaFX objects associated with the FXML elements and
add listeners or handlers:
- Finding the objects is accomplished using the lookup method that
all JavaFX objects inherit from the Node class
- Adding listeners and handlers is most easily accomplished with
lambda expressions
In the VMSS, the following FXML elements were given
id
attributes so that their associated JavaFX objects can be looked up:
- The speaker text area, in order to place simulated speech
there
- The key button grid pane, in order to give each of the key
buttons listeners
- The microphone text area, in order to retrieve simulated
speech
- The send speech and hangup buttons, in order to give them
listeners
The new versions of
Telephone.fxml
and
TelephoneFXML.java are shown in the menu.
To run the FXML version of the VMSS in NetBeans, simply add these files
to the
mailgui package and run the
modified
MailSystemTester.java file.
The current version of
Telephone.fxml does not attempt to style
the font or color of the GUI components.
Below is a rendering of the GUI with the keypad buttons styled using CSS.
This is accomplished by:
- A style class called keyButton defined
in the style sheet vmss.css
- Adding the style sheet as a resource
in MailSystemTesterFXMLGUI.java
- Giving the keypad buttons the keyButton style
in Telephone.fxml
We also remove the
prefWidth and
prefHeight attributes
from the keypad buttons in
Telephone.fxml and give the buttons
their dimensions in
TelephoneFXML.java.
To run this in NetBeans, simply add the
vmss.css file to
the
mailgui package and update the other three files.
See the use of
getStyleSheets near the end of the
start method:
See the use of the
styleClass attribute in the keypad
Button elements:
See the use of the
setPrefSize method in the loop that gives
the keypad buttons their listeners:
User interface
style is concerned with presentation aspects such
as:
- Foreground and background colors of components
- Font styles and sizes
- Gaps and paddings between and around components
- Horizontal and vertical alignments within regions, etc.
Cascading Style Sheets (CSS):
- Provide a mechanism for separating UI data from presentation
(supports MVC)
- Widely used for styling web pages
- Used to style Javascript using JQuery
- Used to style JavaFX scenes
A CSS
style sheet (a file, usually with extension "
.css")
is composed of one or more
CSS rules.
A rule tells how to style either an individual JavaFX scene component
or a group of them.
A rule has the form:
A
selector indicates which scene component, or group of them, is
to be styled, and can be:
- "#<id>" where <id> is an ID programmatically
given to a node in the scene, or
- ".<name>" where <name> is either:
- The (lower-cased) name of a JavaFX control or shape class,
or
- A name given by the styler for a new CSS style
class
Within each rule:
- An attribute-i begins with "-fx-" followed by a JavaFX
property name such as "padding"
- A value-i is an appropriate value for attribute-i,
for example, 0.5em
In the example below (called
scene.css),
- ".label" selects all components that are instances of the
JavaFx Label class
- "#pane" selects the one component in the scene whose ID has been
set to "pane"
- ".buttonrow" selects any component in the scene whose style
class has been set to "buttonrow"
The code below produces the previous
GridPane example, but most
styling has been removed from the Java code. Note the use of:
- pane.setId(...)
- buttons.getStyleClass().add(...)
- scene.getStylesheets().add(...)
This section describes how to lay out a GUI for the Voice Mail System
Simulator (VMSS) using both programmatic JavaFX and FXML.
Central to the VMSS design is the
Telephone
interface, shown below.
This interface allows multiple implementations, including:
- A console-based implementation of the VMSS given
originally
- A programmatic JavaFX GUI implementation, and
- An FXML GUI implementation
The console-based implementation defines a
TelephoneNoGUI class
that implements the
Telephone interface and extends
the
Console class (see menu) introduced in the first lab
exercise.
- It receives input from the keyboard and displays output in a
simulated console window.
- No GUI components make up the interface.
Here are the type relationships:
Select a menu item to the left to access Oracle's online documentation
for JavaFX.
The
Blocks Heuristic Analysis Tool (BHAT) uses JavaFX GUI components.
BHAT allows the user to create initial and goal states for the blocks
world and experiment with different heuristic functions.
Here is an executable JAR file for BHAT:
Heuristic_Analysis_Tool.jar.
Below is a snapshot of BHAT in action. Click
Using BHAT in the
menu to the left to see the source code.