KEMBAR78
JavaFX Already Covered | PDF | Method (Computer Programming) | Java (Programming Language)
0% found this document useful (0 votes)
8 views38 pages

JavaFX Already Covered

The document introduces JavaFX, a next-generation GUI framework for Java, which simplifies the creation of modern, visually appealing user interfaces. It discusses the evolution from AWT to Swing and then to JavaFX, highlighting its features such as lightweight design, MVC architecture support, and automatic rendering. Additionally, it covers the basic structure of a JavaFX application, including lifecycle methods and the use of stages and scenes.

Uploaded by

Manoj Naik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views38 pages

JavaFX Already Covered

The document introduces JavaFX, a next-generation GUI framework for Java, which simplifies the creation of modern, visually appealing user interfaces. It discusses the evolution from AWT to Swing and then to JavaFX, highlighting its features such as lightweight design, MVC architecture support, and automatic rendering. Additionally, it covers the basic structure of a JavaFX application, including lifecycle methods and the use of stages and scenes.

Uploaded by

Manoj Naik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 38

Introducing GUI Programming with JavaFX

Like all successful languages, Java continues to evolve and improve. This also applies to its
libraries. One of the most important examples of this evolutionary process is found in its GUI
frameworks. The original GUI framework was the AWT. Because of its several limitations, it
was soon followed by Swing, which offered a far superior approach to creating GUIs. Swing was
so successful that it has remained the primary Java GUI framework for over a decade. (And a
decade is a long time in the fast-moving world of programming!) However, Swing was designed
when the enterprise application dominated software development. Today, consumer applications,
and especially mobile apps, have risen in importance, and such applications often demand a GUI
that has “visual sparkle.” Furthermore, no matter the type of application, the trend is toward
more exciting visual effects. To better handle these types of GUIs, a new approach was needed,
and this lead to the creation of JavaFX. JavaFX is Java’s next-generation client platform and
GUI framework. JavaFX provides a powerful, streamlined, flexible framework that
simplifies the creation of modern, visually exciting GUIs. As such, it is a very large system,
and, as was the case with Swing, it is not possible to describe it fully in this book. Instead, the
purpose of this and the next two chapters is to introduce several of its key features and
techniques. Once you understand the fundamentals, you will find it easy to explore other aspects
of JavaFX on your own.
The original JavaFX was based on a scripting language called JavaFX Script. However,
JavaFX Script has been discontinued. Beginning with the release of JavaFX 2.0, JavaFX
has been programmed in Java itself and provides a comprehensive API. JavaFX also
supports FXML, which can be (but is not required to be) used to specify the user interface.
JavaFX has been bundled with Java since JDK 7, update 4. The latest version of JavaFX is
JavaFX 8, which is bundled with JDK 8. (The version number is 8 to align with the JDK
version. Thus, the numbers 3 through 7 were skipped.) Because, at the time of this writing,
JavaFX 8 represents the latest version of JavaFX, it is the version of JavaFX discussed here.
Furthermore, when the term JavaFX is used in this and the following chapters, it refers to
JavaFX 8.
NOTE This and the following two chapters assume that you have a basic understanding of event
handling, and Swing fundamentals.

JavaFX Basic Concepts


● In general, the JavaFX framework has all of the good features of Swing. For example,
JavaFX is lightweight. It can also support an MVC architecture.
● From a programmer’s point of view, the first differences you notice between JavaFX and
Swing are the organization of the framework and the relationship of the main components.
● JavaFX offers a more streamlined, easier-to-use, updated approach.
● JavaFX also greatly simplifies the rendering of objects because it handles repainting
automatically. It is no longer necessary for your program to handle this task manually.
● JavaFX facilitates a more visually dynamic approach to GUIs.

The JavaFX Packages


The JavaFX elements are contained in packages that begin with the javafx prefix. At the time of
this writing, there are more than 30 JavaFX packages in its API library. Here are four examples:
javafx.application, javafx.stage, javafx.scene, and javafx.scene.layout. JavaFX offers a wide
array of functionality.
The Stage and Scene Classes
The central metaphor implemented by JavaFX is the stage. As in the case of an actual stage play,
a stage contains a scene. Thus, loosely speaking, a stage defines a space and a scene defines what
goes in that space. Or, put another way, a stage is a container for scenes and a scene is a
container for the items that comprise the scene. As a result, all JavaFX applications have at least
one stage and one scene. These elements are encapsulated in the JavaFX API by the Stage and
Scene classes. To create a JavaFX application, you will, at minimum, add at least one Scene
object to a Stage. Let’s look a bit more closely at these two classes. Stage is a top-level
container. All JavaFX applications automatically have access to one Stage, called the primary
stage. The primary stage is supplied by the run-time system when a JavaFX application is
started. Although you can create other stages, for many applications, the primary stage will be
the only one required. As mentioned, Scene is a container for the items that comprise the scene.
These can consist of controls, such as push buttons and check boxes, text, and graphics. To
create a scene, you will add those elements to an instance of Scene.
Nodes and Scene Graphs
The individual elements of a scene are called nodes. For example, a push button control is a
node. However, nodes can also consist of groups of nodes. Furthermore, a node can have a child
node. In this case, a node with a child is called a parent node or branch node. Nodes without
children are terminal nodes and are called leaves. The collection of all nodes in a scene creates
what is referred to as a scene graph, which comprises a tree. There is one special type of node in
the scene graph, called the root node. This is the top-level node and is the only node in the scene
graph that does not have a parent. Thus, with the exception of the root node, all other nodes have
parents, and all nodes either directly or indirectly descend from the root node. The base class for
all nodes is Node. There are several other classes that are, either directly or indirectly, subclasses
of Node. These include Parent, Group, Region, and Control, to name a few.
Layouts
JavaFX provides several layout panes that manage the process of placing elements in a scene.
For example, the FlowPane class provides a flow layout and the GridPane class supports a
row/column grid-based layout. Several other layouts, such as BorderPane (which is similar to
the AWT’s BorderLayout) are available. The layout panes are packaged in javafx.scene.layout.
The Application Class and the Life-cycle Methods
A JavaFX application must be a subclass of the Application class, which is packaged in
javafx.application. Thus, your application class will extend Application. The Application class
defines three life-cycle methods that your application can override. These are called init( ), start(
), and stop( ), and are shown here, in the order in which they are called:
void init( )
abstract void start(Stage primaryStage)
void stop( )

The init( ) method is called when the application begins execution. It is used to perform various
initializations. As will be explained, however, it cannot be used to create a stage or build a scene.
If no initializations are required, this method need not be overridden because an empty, default
version is provided.
The start( ) method is called after init( ). This is where your application begins and it can be
used to construct and set the scene. Notice that it is passed a reference to a Stage object. This is
the stage provided by the run-time system and is the primary stage. (You can also create other
stages, but you won’t need to for simple applications.) Notice that this method is abstract. Thus,
it must be overridden by your application When your application is terminated, the stop( )
method is called. It is here that you can handle any cleanup or shutdown chores. In cases in
which no such actions are needed, an empty, default version is provided.
Launching a JavaFX Application
To start a free-standing JavaFX application, you must call the launch( ) method defined by
Application. It has two forms. Here is the one used in this chapter:
public static void launch(String ... args)
Here, args is a possibly empty list of strings that typically specify command-line arguments.
When called, launch( ) causes the application to be constructed, followed by calls to init( ) and
start( ). The launch( ) method will not return until after the application has terminated. This
version of launch( ) starts the subclass of Application from which launch( ) is called. The
second form of launch( ) lets you specify a class other than the enclosing class to start. Before
moving on, it is necessary to make an important point: JavaFX applications that have been
packaged by using the javafxpackager tool (or its equivalent in an IDE) do not need to include a
call to launch( ). However, its inclusion often simplifies the test/debug cycle, and it lets the
program be used without the creation of a JAR file. Thus, it is included in all of the JavaFX
programs in this book.
A JavaFX Application Skeleton
All JavaFX applications share the same basic skeleton. Therefore, before looking at any more
JavaFX features, it will be useful to see what that skeleton looks like. In addition to showing the
general form of a JavaFX application, the skeleton also illustrates how to launch the application
and demonstrates when the life-cycle methods are called. A message noting when each life-cycle
method is called is displayed on the console. The complete skeleton is shown here:
// A JavaFX application skeleton.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
public class JavaFXSkel extends Application {
public static void main(String[] args) {
System.out.println("Launching JavaFX application.");
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the init() method.
public void init() { System.out.println("Inside the init() method.");
}
// Override the start() method.
public void start(Stage myStage) {
System.out.println("Inside the start() method.");
// Give the stage a title.
myStage.setTitle("JavaFX Skeleton.");
// Create a root node. In this case, a flow layout pane is used, but several alternatives exist.
FlowPane rootNode = new FlowPane();
// Create a scene.
Scene myScene = new Scene(rootNode, 800, 500);
// Set the scene on the stage.
myStage.setScene(myScene);
// Show the stage and its scene.
myStage.show();
}
// Override the stop() method.
public void stop() {
System.out.println("Inside the stop() method.");
}
}
Although the skeleton is quite short, it can be compiled and run. It produces the
window shown here:

It also produces the following output on the console:


Launching JavaFX application.
Inside the init() method.
Inside the start() method. When you close the window, this message is displayed on the console:
Inside the stop() method.
Of course, in a real program, the life-cycle methods would not normally output anything to
System.out. They do so here simply to illustrate when each method is called. Furthermore, as
explained earlier, you will need to override the init( ) and stop( ) methods only if your
application must perform special startup or shutdown actions. Otherwise, you can use the default
implementations of these methods provided by the Application class.
Let’s examine this program in detail. It begins by importing four packages. The first is
javafx.application, which contains the Application class. The Scene class is packaged in
javafx.scene, and Stage is packaged in javafx.stage. The javafx.scene.layout package provides
several layout panes. The one used by the program is FlowPane. Next, the application class
JavaFXSkel is created. Notice that it extends Application. As explained, Application is the
class from which all JavaFX applications are derived. JavaFXSkel contains two methods. The
first is main( ). It is used to launch the application via a call to launch( ). Notice that the args
parameter to main( ) is passed to the launch( ) method. Although this is a common approach,
you can pass a different set of parameters to launch( ), or none at all. One other point: As
explained earlier, launch( ) is required by a free-standing application, but not in other cases.
When it is not needed, main( ) is also not needed. However, for reasons already explained, both
main( ) and launch( ) are included in the JavaFX programs in this book.
When the application begins, the init( ) method is called first by the JavaFX run-time system.
For the sake of illustration, it simply displays a message on System.out, but it would normally
be used to initialize some aspect of the application. Of course, if no initialization is required, it is
not necessary to override init( ) because an empty, default implementation is provided. It is
important to emphasize that init( ) cannot be used to create the stage or scene portions of a GUI.
Rather, these items should be constructed and displayed by the start( ) method.
After init( ) finishes, the start( ) method executes. It is here that the initial scene is created and
set to the primary stage. Let’s look at this method line-by-line. First, notice that start( ) has a
parameter of type Stage. When start( ) is called, this parameter will receive a reference to the
primary stage of the application. It is to this stage that you will set a scene for the application.
After displaying a message on the console that start( ) has begun execution, it sets the title of the
stage using this call to setTitle( ):

myStage.setTitle("JavaFX Skeleton.");

Although this step is not necessarily required, it is customary for stand-alone applications. This
title becomes the name of the main application window. Next, a root node for a scene is created.
The root node is the only node in a scene graph that does not have a parent. In this case, a
FlowPane is used for the root node, but there are several other classes that can be used for the
root.
FlowPane rootNode = new FlowPane();

As mentioned, a FlowPane is a layout manager that uses a flow layout. This is a layout in which
elements are positioned line-by-line, with lines wrapping as needed. (Thus, it works much like
the FlowLayout class used by the AWT and Swing.) In this case, a horizontal flow is used, but it
is possible to specify a vertical flow. Although not needed by this skeletal application, it is also
possible to specify other layout properties, such as a vertical and horizontal gap between
elements, and an alignment. You will see an example of this later in this chapter.

The following line uses the root node to construct a Scene:


Scene myScene = new Scene(rootNode, 300, 200);

Scene provides several versions of its constructor. The one used here creates a scene that has the
specified root with the specified width and height. It is shown here:

Scene(Parent rootnode, double width, double height)

Notice that the type of rootnode is Parent. It is a subclass of Node and encapsulates nodes that
can have children. Also notice that the width and the height are double values. This lets you pass
fractional values, if needed. In the skeleton, the root is rootNode, the width is 300 and the height
is 200.
The next line in the program sets myScene as the scene for myStage:
myStage.setScene(myScene);
Here, setScene( ) is a method defined by Stage that sets the scene to that specified by its
argument. In cases in which you don’t make further use of the scene, you can combine the
previous two steps, as shown here:
myStage.setScene(new Scene(rootNode, 300, 200));
Because of its compactness, this form will be used by most of the subsequent examples. The last
line in start( ) displays the stage and its scene:
myStage.show();
In essence, show( ) shows the window that was created by the stage and screen. When you close
the application, its window is removed from the screen and the stop( ) method is called by the
JavaFX run-time system. In this case, stop( ) simply displays a message on the console,
illustrating when it is called. However, stop( ) would not normally display anything.
Furthermore, if your application does not need to handle any shutdown actions, there is no
reason to override stop( ) because an empty, default implementation is provided.

Compiling and Running a JavaFX Program

One important advantage of JavaFX is that the same program can be run in a variety of different
execution environments. For example, you can run a JavaFX program as a stand-alone desktop
application, inside a web browser, or as a Web Start application. However, different ancillary
files may be needed in some cases, for example, an HTML file or a JavaNetwork Launch
Protocol (JNLP) file.
In general, a JavaFX program is compiled like any other Java program. However, because of the
need for additional support for various execution environments, the easiest way to compile a
JavaFX application is to use an Integrated Development Environment (IDE) that fully supports
JavaFX programming, such as NetBeans. Just follow the instructions for the IDE you are using.
Alternatively, if you want to compile and test a JavaFX application using the commandline tools,
you can easily do so. Just compile and run the application in the normal way, using javac and
java. Be aware that using the command-line compiler neither creates any HTML or JNLP files
that would be needed if you want to run the application in a way other than as a stand-alone
application, nor does it create a JAR file for the program. To create these files, you need to use a
tool such as javafxpackager.
The Application Thread
In the preceding discussion, it was mentioned that you cannot use the init( ) method to construct
a stage or scene. You also cannot create these items inside the application’s constructor. The
reason is that a stage or scene must be constructed on the application thread. However, the
application’s constructor and the init( ) method are called on the main thread, also called the
launcher thread. Thus, they can’t be used to construct a stage or scene. Instead, you must use the
start( ) method, as the skeleton demonstrates, to create the initial GUI because start( ) is called
on the application thread.
Furthermore, any changes to the GUI currently displayed must be made from the application
thread. Fortunately, in JavaFX, events are sent to your program on the application thread.
Therefore, event handlers can be used to interact with the GUI. The stop( ) method is also called
on the application thread.
A Simple JavaFX Control: Label
The primary ingredient in most user interfaces is the control because a control enables the user to
interact with the application. As you would expect, JavaFX supplies a rich assortment of
controls. The simplest control is the label because it just displays a message, which, in this
example, is text. Although quite easy to use, the label is a good way to introduce the techniques
needed to begin building a scene graph.
The JavaFX label is an instance of the Label class, which is packaged in javafx.scene.control.
Label inherits Labeled and Control, among other classes. The Labeled class defines several
features that are common to all labeled elements (that is, those that can contain text), and
Control defines features related to all controls.
Label defines three constructors. The one we will use here is
Label(String str)
Here, str is the string that is displayed.
Once you have created a label (or any other control), it must be added to the scene’s content,
which means adding it to the scene graph. To do this, you will first call getChildren( ) on the
root node of the scene graph. It returns a list of the child nodes in the form of an
ObservableList<Node>. ObservableList is packaged in javafx.collections, and it inherits
java.util.List, which means that it supports all of the features available to a list as defined by the
Collections Framework. Using the returned list of child nodes, you can add the label to the list by
calling add( ), passing in a reference to the label.
The following program puts the preceding discussion into action by creating a simple JavaFX
application that displays a label:
// Demonstrate a JavaFX label.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
public class JavaFXLabelDemo extends Application {
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Demonstrate a JavaFX label.");
// Use a FlowPane for the root node.
FlowPane rootNode = new FlowPane();
// Create a scene.
Scene myScene = new Scene(rootNode, 300, 200);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a label.
Label myLabel = new Label("This is a JavaFX label");
// Add the label to the scene graph.
rootNode.getChildren().add(myLabel);
// Show the stage and its scene.
myStage.show();
}
}
This program produces the following window:

In the program, pay special attention to this line:


rootNode.getChildren().add(myLabel);
It adds the label to the list of children for which rootNode is the parent. Although this line could
be separated into its individual pieces if necessary, you will often see it as shown here. Before
moving on, it is useful to point out that ObservableList provides a method called addAll( ) that
can be used to add two or more children to the scene graph in a single call. (You will see an
example of this shortly.) To remove a control from the scene graph, call remove( ) on the
ObservableList. For example,
rootNode.getChildren().remove(myLabel);
removes myLabel from the scene.

Using Buttons and Events


Most GUI controls generate events that are handled by your program. For example, buttons,
check boxes, and lists all generate events when they are used. In many ways, event handling in
JavaFX is similar to event handling in Swing or the AWT, but it’s more streamlined. One
commonly used control is the button. This makes button events one of the most frequently
handled. Therefore, a button is a good way to demonstrate the fundamentals of event handling in
JavaFX. For this reason, the fundamentals of event handling and the button are introduced
together.
Event Basics
The base class for JavaFX events is the Event class, which is packaged in javafx.event. Event
inherits java.util.EventObject, which means that JavaFX events share the same basic
functionality as other Java events. Several subclasses of Event are defined. The one that we will
use here is ActionEvent. It handles action events generated by a button. In general, JavaFX uses
what is, in essence, the delegation event model approach to event handling. To handle an event,
you must first register the handler that acts as a listener for the event. When the event occurs, the
listener is called. It must then respond to the event and return. In this regard, JavaFX events are
managed much like Swing events, for example. Events are handled by implementing the
EventHandler interface, which is also in javafx.event. It is a generic interface with the
following form:
interface EventHandler<T extends Event>
Here, T specifies the type of event that the handler will handle. It defines one method, called
handle( ), which receives the event object as a parameter. It is shown here:

void handle(T eventObj)


Here, eventObj is the event that was generated. Typically, event handlers are implemented
through anonymous inner classes or lambda expressions, but you can use stand-alone classes for
this purpose if it is more appropriate to your application (for example, if one event handler will
handle events from more than one source).
Although not required by the examples in this chapter, it is sometimes useful to know the source
of an event. This is especially true if you are using one handler to handle events from different
sources. You can obtain the source of the event by calling getSource( ), which is inherited from
java.util.EventObject. It is shown here:
Object getSource( )
Other methods in Event let you obtain the event type, determine if the event has been consumed,
consume an event, fire an event, and obtain the target of the event. When an event is consumed,
it stops the event from being passed to a parent handler.
One last point: In JavaFX, events are processed via an event dispatch chain. When an event is
generated, it is passed to the root node of the chain. The event is then passed down the chain to
the target of the event. After the target node processes the event, the event is passed back up the
chain, thus allowing parent nodes a chance to process the event, if necessary. This is called event
bubbling. It is possible for a node in the chain to consume an event, which prevents it from being
further processed.
NOTE Although not used in this introduction to JavaFX, an application can also implement an
event filter, which can be used to manage events. A filter is added to a node by calling
addEventFilter( ), which is defined by Node. A filter can consume an event, thus preventing
further processing.
Introducing the Button Control
In JavaFX, the push button control is provided by the Button class, which is in javafx.scene.-
control. Button inherits a fairly long list of base classes that include ButtonBase, Labeled,
Region, Control, Parent, and Node. If you examine the API documentation for Button, you
will see that much of its functionality comes from its base classes. Furthermore, it supports a
wide array of options. However, here we will use its default form. Buttons can contain text,
graphics, or both. In this chapter, we will use text-based buttons. An example of a graphicsbased
button is shown in the next chapter.
Button defines three constructors. The one we will use is shown here:
Button(String str)
In this case, str is the message that is displayed in the button. When a button is pressed, an
ActionEvent is generated. ActionEvent is packaged in javafx.event. You can register a listener
for this event by using setOnAction( ), which has this general form:

final void setOnAction(EventHandler<ActionEvent>handler)

Here, handler is the handler being registered. As mentioned, often you will use an anonymous
inner class or lambda expression for the handler. The setOnAction( ) method sets the property
onAction, which stores a reference to the handler. As with all other Java event handling, your
handler must respond to the event as fast as possible and then return. If your handler consumes
too much time, it will noticeably slow down the application. For lengthy operations, you must
use a separate thread of execution.

Demonstrating Event Handling and the Button


The following program demonstrates event handling. It uses two buttons and a label. Each
time a button is pressed, the label is set to display which button was pressed.
// Demonstrate JavaFX events and buttons.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
public class JavaFXEventDemo extends Application {
Label response;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Demonstrate JavaFX Buttons and Events.");
// Use a FlowPane for the root node. In this case,
// vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 300, 100);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a label.
response = new Label("Push a Button");
// Create two push buttons.
Button btnAlpha = new Button("Alpha");
Button btnBeta = new Button("Beta");
// Handle the action events for the Alpha button.
btnAlpha.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Alpha was pressed.");
}
});
// Handle the action events for the Beta button.
btnBeta.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Beta was pressed.");
}
});
// Add the label and buttons to the scene graph.
rootNode.getChildren().addAll(btnAlpha, btnBeta, response);
// Show the stage and its scene.
myStage.show();
}
}
Sample output from this program is shown here:

Let’s examine a few key portions of this program. First, notice how buttons are createdby these
two lines:
Button btnAlpha = new Button("Alpha");
Button btnBeta = new Button("Beta");
This creates two text-based buttons. The first displays the string Alpha; the second displays Beta.
Next, an action event handler is set for each of these buttons. The sequence for the Alpha button
is shown here:
// Handle the action events for the Alpha button.
btnAlpha.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Alpha was pressed.");
}
});
As explained, buttons respond to events of type ActionEvent. To register a handler for these
events, the setOnAction( ) method is called on the button. It uses an anonymous inner class to
implement the EventHandler interface. (Recall that EventHandler defines only the handle( )
method.) Inside handle( ), the text in the response label is set to reflect the fact that the Alpha
button was pressed. Notice that this is done by calling the setText( ) method on the label. Events
are handled by the Beta button in the same way. Note that response is declared as a field within
FXEventDemo, rather than as a local variable. This is because it is accessed within the button
event handlers, which are anonymous inner classes. After the event handlers have been set, the
response label and the buttons btnAlpha and btnBeta are added to the scene graph by using a
call to addAll( ):
rootNode.getChildren().addAll(btnAlpha, btnBeta, response);
The addAll( ) method adds a list of nodes to the invoking parent node. Of course, these nodes
could have been added by three separate calls to add( ), but the addAll( ) method is more
convenient to use in this situation. There are two other things of interest in this program that
relate to the way the controls are displayed in the window. First, when the root node is created,
this statement is used:
FlowPane rootNode = new FlowPane(10, 10);
Here, the FlowPane constructor is passed two values. These specify the horizontal and vertical
gap that will be left around elements in the scene. If these gaps are not specified, then two
elements (such as two buttons) would be positioned in such a way that no space is between them.
Thus, the controls would run together, creating a very unappealing user interface. Specifying
gaps prevents this. The second point of interest is the following line, which sets the alignment of
the elements in the FlowPane:
rootNode.setAlignment(Pos.CENTER);
Here, the alignment of the elements is centered. This is done by calling setAlignment( ) on the
FlowPane. The value Pos.CENTER specifies that both a vertical and horizontal center will be
used. Other alignments are possible. Pos is an enumeration that specifies alignment constants. It
is packaged in javafx.geometry.
Before moving on, one more point needs to be made. The preceding program used anonymous
inner classes to handle button events. However, because the EventHandler interface defines
only one abstract method, handle( ), a lambda expression could have passed to setOnAction( ),
instead. In this case, the parameter type of setOnAction( ) would supply the target context for
the lambda expression. For example, here is the handler for the Alpha button, rewritten to use a
lambda:
btnAlpha.setOnAction( (ae) ->
response.setText("Alpha was pressed.")
);
Notice that the lambda expression is more compact than the anonymous inner class. Because
lambda expressions are a new feature just recently added to Java, but the anonymous inner class
is a widely used construct, readily understood by nearly all Java programmers, the event handlers
in subsequent examples will use anonymous inner classes. This will also allow the examples to
be compiled by readers using JDK 7 (which does not support lambdas). However, on your own,
you might want to experiment with converting them to lambda expressions. It is a good way to
gain experience using lambdas in your own code.

Drawing Directly on a Canvas

As mentioned early on, JavaFX handles rendering tasks for you automatically, rather than you
handling them manually. This is one of the most important ways that JavaFX improves on
Swing. As you may know, in Swing or the AWT, you must call the repaint( ) method to cause a
window to be repainted. Furthermore, your application needs to store the window contents,
redrawing them when painting is requested. JavaFX eliminates this tedious mechanism because
it keeps track of what you display in a scene and redisplays that scene as needed. This is called
retained mode. With this approach, there is no need to call a method like repaint( ). Rendering is
automatic.
One place that JavaFX’s approach to rendering is especially helpful is when displayinggraphics
objects, such as lines, circles, and rectangles. JavaFX’s graphics methods are foundin the
GraphicsContext class, which is part of java.scene.canvas. These methods can beused to draw
directly on the surface of a canvas, which is encapsulated by the Canvas classin
java.scene.canvas. When you draw something, such as a line, on a canvas, JavaFX
automatically renders it whenever it needs to be redisplayed.
Before you can draw on a canvas, you must perform two steps. First, you must create aCanvas
instance. Second, you must obtain a GraphicsContext object that refers to thatcanvas. You can
then use the GraphicsContext to draw output on the canvas.The Canvas class is derived from
Node; thus it can be used as a node in a scene graph.
Canvas defines two constructors. One is the default constructor, and the other is the oneshown
here:
Canvas(double width, double height)
Here, width and height specify the dimensions of the canvas.
To obtain a GraphicsContext that refers to a canvas, call getGraphicsContext2D( ).Here is its
general form:
GraphicsContext getGraphicsContext2D( )

The graphics context for the canvas is returned.


GraphicsContext defines a large number of methods that draw shapes, text, and images,and
support effects and transforms. If sophisticated graphics programming is in your future,you will
definitely want to study its capabilities closely. For our purposes, we will use only afew of its
methods, but they will give you a sense of its power. They are described here.
You can draw a line using strokeLine( ), shown here:

void strokeLine(double startX, double startY, double endX, double endY)

It draws a line from startX,startY to endX,endY, using the current stroke, which can be a
solidcolor or some more complex style.
To draw a rectangle, use either strokeRect( ) or fillRect( ), shown here:

void strokeRect(double topX, double topY, double width, double height)


void fillRect(double topX, double topY, double width, double height)

The upper-left corner of the rectangle is at topX,topY. The width and height parameters specify
its width and height. The strokeRect( ) method draws the outline of a rectangle using stroke,
and fillRect( ) fills the rectangle with the current fill. The current fill can beas simple as a solid
color or something more complex.
To draw an ellipse, use either strokeOval( ) or fillOval( ), shown next:

void strokeOval(double topX, double topY, double width, double height)


void fillOval(double topX, double topY, double width, double height)

The upper-left corner of the rectangle that bounds the ellipse is at topX,topY. The width and
height parameters specify its width and height. The strokeOval( ) method draws the outline of
an ellipse using the current stroke, and fillOval( ) fills the oval with the current fill. To draw a
circle, pass the same value for width and height.
You can draw text on a canvas by using the strokeText( ) and fillText( ) methods. We
will use this version of fillText( ):

void fillText(String str, double topX, double topY)

It displays str starting at the location specified by topX,topY, filling the text with the current
fill.You can set the font and font size of the text being displayed by using setFont( ). You can
obtain the font used by the canvas by calling getFont( ). By default, the system font is used. You
can create a new font by constructing a Font object. Font is packaged in javafx.scene.text. For
example, you can create a default font of a specified size by using this constructor:
Font(double fontSize)
Here, fontSize specifies the size of the font.

You can specify the fill and stroke using these two methods defined by Canvas:
void setFill(Paint newFill)
void setStroke(Paint newStroke)
Notice that the parameter of both methods is of type Paint. This is an abstract class packaged in
javafx.scene.paint. Its subclasses define fills and strokes. The one we will use is Color, which
simply describes a solid color. Color defines several static fields that specify a wide array of
colors, such as Color.BLUE, Color.RED, Color.GREEN, and so on.
The following program uses the aforementioned methods to demonstrate drawing on a canvas. It
first displays a few graphic objects on the canvas. Then, each time the Change Color button is
pressed, the color of three of the objects changes color. If you run the program,you will see that
the shapes whose color is not changed are unaffected by the change in color of the other objects.
Furthermore, if you try covering and then uncovering the window,
you will see that the canvas is automatically repainted, without any other actions on the part
of your program. Sample output is shown here:

// Demonstrate drawing.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.shape.*;
import javafx.scene.canvas.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
public class DirectDrawDemo extends Application {
GraphicsContext gc;
Color[] colors = { Color.RED, Color.BLUE, Color.GREEN, Color.BLACK };
int colorIdx = 0;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Draw Directly to a Canvas.");
// Use a FlowPane for the root node.
FlowPane rootNode = new FlowPane();
// Center the nodes in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 450, 450);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a canvas.
Canvas myCanvas = new Canvas(400, 400);
// Get the graphics context for the canvas.
gc = myCanvas.getGraphicsContext2D();
// Create a push button.
Button btnChangeColor = new Button("Change Color");
// Handle the action events for the Change Color button.
btnChangeColor.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
// Set the stroke and fill color.
gc.setStroke(colors[colorIdx]);
gc.setFill(colors[colorIdx]);
// Redraw the line, text, and filled rectangle in the new color. This leaves the color of the
other //nodes unchanged.
gc.strokeLine(0, 0, 200, 200);
gc.fillText("This is drawn on the canvas.", 60, 50);
gc.fillRect(100, 320, 300, 40);
// Change the color.
colorIdx++;
if(colorIdx == colors.length) colorIdx= 0;
}
});
// Draw initial output on the canvas.
gc.strokeLine(0, 0, 200, 200);
gc.strokeOval(100, 100, 200, 200);
gc.strokeRect(0, 200, 50, 200);
gc.fillOval(0, 0, 20, 20);
gc.fillRect(100, 320, 300, 40);
// Set the font size to 20 and draw text.
gc.setFont(new Font(20));
gc.fillText("This is drawn on the canvas.", 60, 50);
// Add the canvas and button to the scene graph.
rootNode.getChildren().addAll(myCanvas, btnChangeColor);
// Show the stage and its scene.
myStage.show();
}
}
It is important to emphasize that GraphicsContext supports many more operations thanthose
demonstrated by the preceding program. For example, you can apply varioustransforms,
rotations, and effects. Despite its power, its various features are easy to masterand use. One other
point: A canvas is transparent. Therefore, if you stack canvases, thecontents of both will show.
This may be useful in some situations.
NOTE javafx.scene.shape contains several classes that can also be used to draw various types of
graphical shapes, such as circles, arcs, and lines. These are represented by nodes and can,
therefore,be directly part of the scene graph. You will want to explore these on your own.

Exploring JavaFXControls

The previous chapter described several of the core concepts relating to the JavaFX GUI
framework. In the process, it introduced two controls: the label and the button. This chapter
continues the discussion of JavaFX controls. It begins by describing how to include images in a
label and button. It then presents an overview of several more JavaFX controls, including check
boxes, lists, and trees. Keep in mind that JavaFX is a rich and powerful framework.The purpose
of this chapter is to introduce a representative sampling of the JavaFX controls and to describe
several common techniques. Once you understand the basics, you will be able to easily learn the
other controls.
The JavaFX control classes discussed in this chapter are shown here:

These and the other JavaFX controls are packaged in javafx.scene.control. Also discussed are
the Image and ImageView classes, which provide support for images in controls; Tooltip,
which is used to add tooltips to a control; as well as various effects and transforms.
Using Image and ImageView

Several of JavaFX’s controls let you include an image. For example, in addition to text, you can
specify an image in a label or a button. Furthermore, you can embed stand-alone images in a
scene directly. At the foundation for JavaFX’s support for images are two classes: Image and
ImageView. Image encapsulates the image, itself, and ImageView manages the display of an
image. Both classes are packaged in javafx.scene.image.The Image class loads an image from
either an InputStream, a URL, or a path to the image file. Image defines several constructors;
this is the one we will use:

Image(String url)

Here, url specifies a URL or a path to a file that supplies the image. The argument is assumed
to refer to a path if it does not constitute a properly formed URL. Otherwise, the image is loaded
from the URL. The examples that follow will load images from files on the local filesystem.
Other constructors let you specify various options, such as the image’s width and height. One
other point: Image is not derived from Node. Thus, it cannot, itself, be part of a scene graph.
Once you have an Image, you will use ImageView to display it. ImageView is derived from
Node, which means that it can be part of a scene graph. ImageView defines three constructors.
The first one we will use is shown here:

ImageView(Image image)

This constructor creates an ImageView that uses image for its image. Putting the preceding
discussion into action, here is a program that loads an image of an hourglass and displays it via
ImageView. The hourglass image is contained in a file called hourglass.png, which is assumed
to be in the local directory.
// Load and display an image.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.geometry.*;
import javafx.scene.image.*;
public class ImageDemo extends Application {
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Display an Image");
// Use a FlowPane for the root node.
FlowPane rootNode = new FlowPane();
// Use center alignment.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 300, 200);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create an image.
Image hourglass = new Image("hourglass.png");
// Create an image view that uses the image.
ImageView hourglassIV = new ImageView(hourglass);
// Add the image to the scene graph.
rootNode.getChildren().add(hourglassIV);
// Show the stage and its scene.
myStage.show();
}
}
Sample output from the program is shown here:

In the program, pay close attention to the following sequence that loads the image andthen
creates an ImageView that uses that image:
// Create an image.
Image hourglass = new Image("HourGlass.png");
// Create an image view that uses the image.
ImageView hourglassIV = new ImageView(hourglass);
As explained, an image by itself cannot be added to the scene graph. It must first beembedded in
an ImageView.In cases in which you won’t make further use of the image, you can specify a
URL orfilename when creating an ImageView. In this case, there is no need to explicitly create
anImage. Instead, an Image instance containing the specified image is constructed
automaticallyand embedded in the ImageView. Here is the ImageView constructor that does
this:
ImageView(String url)

Here, url specifies the URL or the path to a file that contains the image.
Adding an Image to a Label
As explained in the previous chapter, the Label class encapsulates a label. It can display a text
message, a graphic, or both. So far, we have used it to display only text, but it is easy to add an
image. To do so, use this form of Label’s constructor:
Label(String str, Node image)
Here, str specifies the text message and image specifies the image. Notice that the image is of
type Node. This allows great flexibility in the type of image added to the label, but for our
purposes, the image type will be ImageView. Here is a program that demonstrates a label that
includes a graphic. It creates a label that displays the string "Hourglass" and shows the image of
an hourglass that is loaded from
the hourglass.png file.
// Demonstrate an image in a label.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;
import javafx.scene.image.*;
public class LabelImageDemo extends Application {
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Use an Image in a Label");
// Use a FlowPane for the root node.
FlowPane rootNode = new FlowPane();
// Use center alignment.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 300, 200);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create an ImageView that contains the specified image.
ImageView hourglassIV = new ImageView("hourglass.png");
// Create a label that contains both an image and text.
Label hourglassLabel = new Label("Hourglass", hourglassIV);
// Add the label to the scene graph.
rootNode.getChildren().add(hourglassLabel);
// Show the stage and its scene.
myStage.show();
}
}
Here is the window produced by the program:

As you can see, both the image and the text are displayed. Notice that the text is to the right of
the image. This is the default. You can change the relative positions of the image and text by
calling setContentDisplay( ) on the label. It is shown here:
final void setContentDisplay(ContentDisplay position)
The value passed to position determines how the text and image is displayed. It must be one of
these values, which are defined by the ContentDisplay enumeration:

With the exception of TEXT_ONLY and GRAPHIC_ONLY, the values specify the locationof
the image. For example, if you add this line to the preceding
program:hourglassLabel.setContentDisplay(ContentDisplay.TOP);the image of the hourglass
will be above the text, as shown here:

The other two values let you display either just the text or just the image. This might be useful if
your application wants to use an image at some times, and not at others, for example.(If you want
only an image, you can simply display it without using a label, as described in the previous
section.)You can also add an image to a label after it has been constructed by using the
setGraphic( ) method. It is shown here:
final void setGraphic(Node image)
Here, image specifies the image to add.
Using an Image with a Button
Button is JavaFX’s class for push buttons. The procedure for adding an image to a button is
similar to that used to add an image to a label. First obtain an ImageView of the image. Then
add it to the button. One way to add the image is to use this constructor:

Button(String str, Node image)

Here, str specifies the text that is displayed within the button and image specifies the image. You
can specify the position of the image relative to the text by using setContentDisplay( )in the
same way as just described for Label. Here is an example that displays two buttons that contain
images. The first shows an hourglass. The second shows an analog clock. When a button is
pressed, the selected timepiece is reported. Notice that the text is displayed beneath the image.
// Use an image with a button.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.image.*;
public class ButtonImageDemo extends Application {
Label response;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Use Images with Buttons");
// Use a FlowPane for the root node. In this case,
// vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 250, 450);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a label.
response = new Label("Push a Button");
// Create two image-based buttons.
Button btnHourglass = new Button("Hourglass",new ImageView("hourglass.png"));
Button btnAnalogClock = new Button("Analog Clock", new ImageView("analog.png"));
// Position the text under the image.
btnHourglass.setContentDisplay(ContentDisplay.TOP);
btnAnalogClock.setContentDisplay(ContentDisplay.TOP);
// Handle the action events for the hourglass button.
btnHourglass.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Hourglass Pressed");
}
});
// Handle the action events for the analog clock button.
btnAnalogClock.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Analog Clock Pressed");
}
});
// Add the label and buttons to the scene graph.
rootNode.getChildren().addAll(btnHourglass, btnAnalogClock, response);
// Show the stage and its scene.
myStage.show();
}
}
The output produced by this program is shown here:

If you want a button that contains only the image, pass a null string for the text whenconstructing
the button and then call setContentDisplay( ), passing in the
parameterContentDisplay.GRAPHIC_ONLY. For example, if you make these modifications to
theprevious program, the output will look like this:

ToggleButton

A useful variation on the push button is called the toggle button. A toggle button looks just like a
push button, but it acts differently because it has two states: pushed and released. That is, when
you press a toggle button, it stays pressed rather than popping back up as a regular push button
does. When you press the toggle button a second time, it releases(pops up). Therefore, each time
a toggle button is pushed, it toggles between these two states. In JavaFX, a toggle button is
encapsulated in the ToggleButton class. Like Button,ToggleButton is also derived from
ButtonBase. It implements the Toggle interface, which defines functionality common to all
types of two-state buttons.
ToggleButton defines three constructors. This is the one we will use:

ToggleButton(String str)

Here, str is the text displayed in the button. Another constructor allows you to include an image.
Like other buttons, a ToggleButton generates an action event when it is pressed. Because
ToggleButton defines a two-state control, it is commonly used to let the user select an option.
When the button is pressed, the option is selected. When the button is released, the option is
deselected. For this reason, a program usually needs to determine the toggle button’s state. To do
this, use the isSelected( ) method, shown here:

final boolean isSelected( )


It returns true if the button is pressed and false otherwise.

Here is a short program that demonstrates ToggleButton:


// Demonstrate a toggle button.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
public class ToggleButtonDemo extends Application {
ToggleButton tbOnOff;
Label response;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Demonstrate a Toggle Button");
// Use a FlowPane for the root node. In this case,
// vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 220, 120);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a label.
response = new Label("Push the Button.");
// Create the toggle button.
tbOnOff = new ToggleButton("On/Off");
// Handle action events for the toggle button.
tbOnOff.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
if(tbOnOff.isSelected()) response.setText("Button is on.");
else response.setText("Button is off.");
}
});
// Add the label and buttons to the scene graph.
rootNode.getChildren().addAll(tbOnOff, response);
// Show the stage and its scene.
myStage.show();
}
}
Sample output produced by the program is shown here,with the button pressed:In the program,
notice how the pressed/released state ofthe toggle button is determined by the following lines of
codeinside the button’s action event handler.if(tbOnOff.isSelected()) response.setText("Button is
on.");else response.setText("Button is off.");When the button is pressed, isSelected( ) returns
true. When the button is released,isSelected( ) returns false.
One other point: It is possible to use two or more toggle buttons in a group. In thiscase, only one
button can be in its pressed state at any one time. The process of creatingand using a group of
toggle buttons is similar to that required to use radio buttons. It isdescribed in the following
section.

RadioButton
Another type of button provided by JavaFX is the radio button. Radio buttons are a group of
mutually exclusive buttons, in which only one button can be selected at any one time. They are
supported by the RadioButton class, which extends both ButtonBase and ToggleButton. It also
implements the Toggle interface. Thus, a radio button is a specialized form of a togglebutton. To
create a radio button, we will use the following constructor:

RadioButton(String str)
Here, str is the label for the button. Like other buttons, when a RadioButton is used, an action
event is generated.For their mutually exclusive nature to be activated, radio buttons must be
configured into a group. Only one of the buttons in the group can be selected at any time. For
example,if a user presses a radio button that is in a group, any previously selected button in
thatgroup is automatically deselected. A button group is created by the ToggleGroup
class,which is packaged in javafx.scene.control. ToggleGroup provides only a default
constructor.Radio buttons are added to the toggle group by calling the setToggleGroup( )
method,defined by ToggleButton, on the button. It is shown here:

final void setToggleGroup(ToggleGroup tg)

Here, tg is a reference to the toggle button group to which the button is added. After all radio
buttons have been added to the same group, their mutually exclusive behavior willbe enabled.In
general, when radio buttons are used in a group, one of the buttons is selected when the group is
first displayed in the GUI. Here are two ways to do this.
First, you can call setSelected( ) on the button that you want to select. It is defined
byToggleButton (which is a superclass of RadioButton). It is shown here:

final void setSelected(boolean state)


If state is true, the button is selected. Otherwise, it is deselected. Although the button isselected,
no action event is generated.A second way to initially select a radio button is to call fire( ) on the
button. It isshown here:
void fire( )
This method results in an action event being generated for the button if the button waspreviously
not selected.
There are a number of different ways to use radio buttons. Perhaps the simplest is tosimply
respond to the action event that is generated when one is selected. The followingprogram shows
an example of this approach. It uses radio buttons to allow the user to selecta type of
transportation.
// A simple demonstration of Radio Buttons.
// This program responds to the action events generated by a radio button selection. It also
shows //how to fire the button under program control.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
public class RadioButtonDemo extends Application {
Label response;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Demonstrate Radio Buttons");
// Use a FlowPane for the root node. In this case, vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 220, 120);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a label that will report the selection.
response = new Label("");
// Create the radio buttons.
RadioButton rbTrain = new RadioButton("Train");
RadioButton rbCar = new RadioButton("Car");
RadioButton rbPlane = new RadioButton("Airplane");
// Create a toggle group.
ToggleGroup tg = new ToggleGroup();
// Add each button to a toggle group.
rbTrain.setToggleGroup(tg);
rbCar.setToggleGroup(tg);
rbPlane.setToggleGroup(tg);
// Handle action events for the radio buttons.
rbTrain.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Transport selected is train.");
}
});
rbCar.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Transport selected is car.");
}
});
rbPlane.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
response.setText("Transport selected is airplane.");
});
// Fire the event for the first selection. This causes that radio button to be selected and an
action //event for that button to occur.
rbTrain.fire();
// Add the label and buttons to the scene graph.
rootNode.getChildren().addAll(rbTrain, rbCar, rbPlane, response);
// Show the stage and its scene.
myStage.show();
}
}
Sample output is shown here:

In the program, pay special attention to how the radiobuttons and the toggle group are created.
First, the buttonsare created using this sequence:
RadioButton rbTrain = new RadioButton("Train");
RadioButton rbCar = new RadioButton("Car");
RadioButton rbPlane = new RadioButton("Airplane");
Next, a ToggleGroup is constructed:
ToggleGroup tg = new ToggleGroup();
Finally, each radio button is added to the toggle group:
rbTrain.setToggleGroup(tg);
rbCar.setToggleGroup(tg);
rbPlane.setToggleGroup(tg);
As explained, radio buttons must be part of a toggle group in order for their mutuallyexclusive
behavior to be activated.
After the event handlers for each radio button have been defined, the rbTrain button isselected
by calling fire( ) on it. This causes that button to be selected and an action event tobe generated
for it. This causes the button to be initialized with the default selection.

An Alternative Way to Handle Radio Buttons


Although handling events generated by radio buttons is often useful, sometimes it is
moreappropriate to ignore those events and simply obtain the currently selected button whenthat
information is needed. This approach is demonstrated by the following program. Itadds a button
called Confirm Transport Selection. When this button is pressed, the currentlyselected radio
button is obtained and then the selected transport is displayed in a label.When you try the
program, notice that changing the selected radio button does not causethe confirmed transport to
change until you press the Confirm Transport Selection button.
// This radio button example demonstrates how the
// currently selected button in a group can be obtained
// under program control, when it is needed, rather
// than responding to action or change events.
//
// In this example, no events related to the radio
// buttons are handled. Instead, the current selection
// is simply obtained when the Confirm Transport Selection push
// button is pressed.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
public class RadioButtonDemo2 extends Application {
Label response;
ToggleGroup tg;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Demonstrate Radio Buttons");
// Use a FlowPane for the root node. In this case,
// vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 200, 140);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create two labels.
Label choose = new Label(" Select a Transport Type ");
response = new Label("No transport confirmed");
// Create push button used to confirm the selection.
Button btnConfirm = new Button("Confirm Transport Selection");
// Create the radio buttons.
RadioButton rbTrain = new RadioButton("Train");
RadioButton rbCar = new RadioButton("Car");
RadioButton rbPlane = new RadioButton("Airplane");
// Create a toggle group.
tg = new ToggleGroup();
// Add each button to a toggle group.
rbTrain.setToggleGroup(tg);
rbCar.setToggleGroup(tg);
rbPlane.setToggleGroup(tg);
// Initially select one of the radio buttons.
rbTrain.setSelected(true);
// Handle action events for the confirm button.
btnConfirm.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
// Get the radio button that is currently selected.
RadioButton rb = (RadioButton) tg.getSelectedToggle();
// Display the selection.
response.setText(rb.getText() + " is confirmed.");
}
});
// Use a separator to better organize the layout.
Separator separator = new Separator();
separator.setPrefWidth(180);
// Add the label and buttons to the scene graph.
rootNode.getChildren().addAll(choose, rbTrain, rbCar, rbPlane,
separator, btnConfirm, response);
// Show the stage and its scene.
myStage.show();
}
}
The output from the program is shown here:
Most of the program is easy to understand, but two key points are of special interest.First, inside
the action event handler for the btnConfirm button, notice that the selectedradio button is
obtained by the following line:
RadioButton rb = (RadioButton) tg.getSelectedToggle();

Here, the getSelectedToggle( ) method (defined by ToggleGroup) obtains the currentselection


for the toggle group (which, in this case, is a group of radio buttons). It isshown here:

final Toggle getSelectedToggle( )

It returns a reference to the Toggle that is selected. In this case, the return value is cast
toRadioButton because this is the type of button in the group.The second thing to notice is the
use of a visual separator, which is created by thissequence:

Separator separator = new Separator();


separator.setPrefWidth(180);

The Separator class creates a line, which can be either vertical or horizontal. By default,
itcreates a horizontal line. (A second constructor lets you choose a vertical separator.)
Separatorhelps visually organize the layout of controls. It is packaged in javafx.scene.control.
Next,the width of the separator line is set by calling setPrefWidth( ), passing in the width.

CheckBox
The CheckBox class encapsulates the functionality of a check box. Its immediate superclassis
ButtonBase. Although you are no doubt familiar with check boxes because they are widelyused
controls, the JavaFX check box is a bit more sophisticated than you may at first think.This is
because CheckBox supports three states. The first two are checked or unchecked, asyou would
expect, and this is the default behavior. The third state is indeterminate (also calledundefined). It
is typically used to indicate that the state of the check box has not been set orthat it is not
relevant to a specific situation. If you need the indeterminate state, you willneed to explicitly
enable it.
CheckBox defines two constructors. The first is the default constructor. The second letsyou
specify a string that identifies the box. It is shown here:
CheckBox(String str)
It creates a check box that has the text specified by str as a label. As with other buttons,
aCheckBox generates an action event when it is selected.Here is a program that demonstrates
check boxes. It displays check boxes that let theuser select various deployment options, which
are Web, Desktop, and Mobile. Each time acheck box state changes, an action event is generated
and handled by displaying the newstate (selected or cleared) and by displaying a list of all
selected boxes.
// Demonstrate Check Boxes.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.event.*;
import javafx.geometry.*;
public class CheckboxDemo extends Application {
CheckBox cbWeb;
CheckBox cbDesktop;
CheckBox cbMobile;
Label response;
Label allTargets;
String targets = "";
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("Demonstrate Checkboxes");
// Use a FlowPane for the root node. In this case,
// vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 230, 140);
// Set the scene on the stage.
myStage.setScene(myScene);
Label heading = new Label("Select Deployment Options");
// Create a label that will report the state of the
// selected check box.
response = new Label("No Deployment Selected");
// Create a label that will report all targets selected.
allTargets = new Label("Target List: <none>");
// Create the check boxes.
cbWeb = new CheckBox("Web");
cbDesktop = new CheckBox("Desktop");
cbMobile = new CheckBox("Mobile");
// Handle action events for the check boxes.
cbWeb.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
if(cbWeb.isSelected())
response.setText("Web deployment selected.");
else
response.setText("Web deployment cleared.");
showAll();
}
});
cbDesktop.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
if(cbDesktop.isSelected())
response.setText("Desktop deployment selected.");
else
response.setText("Desktop deployment cleared.");
showAll();
}
});
cbMobile.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
if(cbMobile.isSelected())
response.setText("Mobile deployment selected.");
else
response.setText("Mobile deployment cleared.");
showAll();
}
});
// Use a separator to better organize the layout.
Separator separator = new Separator();
separator.setPrefWidth(200);
// Add controls to the scene graph.
rootNode.getChildren().addAll(heading, separator, cbWeb, cbDesktop,
cbMobile, response, allTargets);
// Show the stage and its scene.
myStage.show();
}
// Update and show the targets list.
void showAll() {
targets = "";
if(cbWeb.isSelected()) targets = "Web ";
if(cbDesktop.isSelected()) targets += "Desktop ";
if(cbMobile.isSelected()) targets += "Mobile";
if(targets.equals("")) targets = "<none>";
allTargets.setText("Target List: " + targets);
}
}
Sample output is shown here:
The operation of this program is straightforward. Each time a check box is changed,an action
command is generated. To determine if the box is checked or unchecked, theisSelected( )
method is called.As mentioned, by default, CheckBox implements two states: checked and
unchecked.If you want to add the indeterminate state, it must be explicitly enabled. To do this,
callsetAllowIndeterminate( ), shown here:

final void setAllowIndeterminate(boolean enable)

In this case, if enable is true, the indeterminate state is enabled. Otherwise, it is disabled.When
the indeterminate state is enabled, the user can select between checked, unchecked,and
indeterminate.You can determine if a check box is in the indeterminate state by
callingisIndeterminate( ), shown here:

final boolean isIndeterminate( )


It returns true if the checkbox state is indeterminate and false otherwise.You can see the effect
of a three-state check box by modifying the preceding program.First, enable the indeterminate
state on the check boxes by calling setAllowIndeterminate( )on each check box, as shown here:
cbWeb.setAllowIndeterminate(true);
cbDesktop.setAllowIndeterminate(true);
cbMobile.setAllowIndeterminate(true);
Next, handle the indeterminate state inside the action event handlers. For example, here isthe
modified handler for cbWeb:
cbWeb.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent ae) {
if(cbWeb.isIndeterminate())
response.setText("Web deployment indeterminate.");else if(cbWeb.isSelected())
response.setText("Web deployment selected.");
else
response.setText("Web deployment cleared.");
showAll();
}
});
Now, all three states are tested. Update the other two handlers in the same way. After making
these changes, the indeterminate state can be selected, as this sample output shows:
Here, the Web check box is indeterminate.

ListView
Another commonly used control is the list view, which in JavaFX is encapsulated by
ListView.List views are controls that display a list of entries from which you can select one or
more.Because of their ability to make efficient use of limited screen space, list views are
popularalternatives to other types of selection controls.
ListView is a generic class that is declared like this:
class ListView<T>

Here, T specifies the type of entries stored in the list view. Often, these are entries of typeString,
but other types are also allowed.
ListView defines two constructors. The first is the default constructor, which creates anempty
ListView. The second lets you specify the list of entries in the list. It is shown here:

ListView(ObservableList<T>list)

Here, list specifies a list of the items that will be displayed. It is an object of
typeObservableList, which defines a list of observable objects. It inherits java.util.List. Thus,it
supports the standard collection methods. ObservableList is packaged in
javafx.collections.Probably the easiest way to create an ObservableList for use in a ListView is
to use thefactory method observableArrayList( ), which is a static method defined by the
FXCollectionsclass (which is also packaged in javafx.collections). The version we will use is
shown here:

static <E> ObservableList<E> observableArrayList( E ... elements)

In this case, E specifies the type of elements, which are passed via elements.By default, a
ListView allows only one item in the list to be selected at any one time.
However, you can allow multiple selections by changing the selection mode. For now, wewill
use the default, single-selection model.Although ListView provides a default size, sometimes
you will want to set the preferredheight and/or width to best match your needs. One way to do
this is to call thesetPrefHeight( ) and setPrefWidth( ) methods, shown here:

final void setPrefHeight(double height)


final void setPrefWidth(double width)

Alternatively, you can use a single call to set both dimensions at the same time by use
ofsetPrefSize( ), shown here:
void setPrefSize(double width, double height)

There are two basic ways in which you can use a ListView. First, you can ignore
eventsgenerated by the list and simply obtain the selection in the list when your program needs
it.Second, you can monitor the list for changes by registering a change listener. This lets
yourespond each time the user changes a selection in the list. This is the approach used here.To
listen for change events, you must first obtain the selection model used by theListView. This is
done by calling getSelectionModel( ) on the list. It is shown here:

final MultipleSelectionModel<T> getSelectionModel( )

It returns a reference to the model. MultipleSelectionModel is a class that defines the


modelused for multiple selections, and it inherits SelectionModel. However, multiple
selectionsare allowed in a ListView only if multiple-selection mode is turned on.Using the
model returned by getSelectionModel( ), you will obtain a reference to theselected item property
that defines what takes place when an element in the list is selected.This is done by calling
selectedItemProperty( ), shown next:

final ReadOnlyObjectProperty<T> selectedItemProperty( )


You will add the change listener to this property.
The following example puts the preceding discussion into action. It creates a list viewthat
displays various types of transportation, allowing the user to select one. When one ischosen, the
selection is displayed.
// Demonstrate a list view.
import javafx.application.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;
import javafx.beans.value.*;
import javafx.collections.*;
public class ListViewDemo extends Application {
Label response;
public static void main(String[] args) {
// Start the JavaFX application by calling launch().
launch(args);
}
// Override the start() method.
public void start(Stage myStage) {
// Give the stage a title.
myStage.setTitle("ListView Demo");
// Use a FlowPane for the root node. In this case,
// vertical and horizontal gaps of 10.
FlowPane rootNode = new FlowPane(10, 10);
// Center the controls in the scene.
rootNode.setAlignment(Pos.CENTER);
// Create a scene.
Scene myScene = new Scene(rootNode, 200, 120);
// Set the scene on the stage.
myStage.setScene(myScene);
// Create a label.
response = new Label("Select Transport Type");
// Create an ObservableList of entries for the list view.
ObservableList<String> transportTypes =
FXCollections.observableArrayList( "Train", "Car", "Airplane" );
// Create the list view.
ListView<String> lvTransport = new ListView<String>(transportTypes);
// Set the preferred height and width.
lvTransport.setPrefSize(80, 80);
// Get the list view selection model.
MultipleSelectionModel<String> lvSelModel =
lvTransport.getSelectionModel();
// Use a change listener to respond to a change of selection within
// a list view.
lvSelModel.selectedItemProperty().addListener(
new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> changed,
String oldVal, String newVal) {
// Display the selection.
response.setText("Transport selected is " + newVal);
}
});
rootNode.getChildren().addAll(lvTransport, response);
// Show the stage and its scene.
myStage.show();
}
}
Sample output is shown here:

In the program, pay special attention to how the ListView is constructed. First,
anObservableList is created by this line:
ObservableList<String> transportTypes =
FXCollections.observableArrayList( "Train", "Car", "Airplane" );
It uses the observableArrayList( ) method to create a list of strings. Then, the ObservableList
is used to initialize a ListView, as shown here:
ListView<String> lvTransport = new ListView<String>(transportTypes);
The program then sets the preferred width and height of the control.Now, notice how the
selection model is obtained for lvTransport:
MultipleSelectionModel<String> lvSelModel =lvTransport.getSelectionModel();
As explained, ListView uses MultipleSelectionModel, even when only a single selection
isallowed. The selectedItemProperty( ) method is then called on the model and a changelistener
is registered to the returned item.

ListView Scrollbars
One very useful feature of ListView is that when the number of items in the list exceeds
thenumber that can be displayed within its dimensions, scrollbars are automatically added.
Forexample, if you change the declaration of transportTypes so that it includes "Bicycle"
and"Walking", as shown here:
ObservableList<String> transportTypes =
FXCollections.observableArrayList( "Train", "Car", "Airplane",
"Bicycle", "Walking" );
the lvTransport control now looks like the one shown here:

Enabling Multiple Selections


If you want to allow more than one item to be selected, you must explicitly request it.To do so,
you must set the selection mode to SelectionMode.MULTIPLE by callingsetSelectionMode( )
on the ListView model. It is shown here:
final void setSelectionMode(SelectionMode mode)
In this case, mode must be either SelectionMode.MULTIPLE or
SelectionMode.SINGLE.When multiple-selection mode is enabled, you can obtain the list of
the selections twoways: as a list of selected indices or as a list of selected items. We will use a
list of selecteditems, but the procedure is similar when using a list of the indices of the selected
items.(Note, indexing of items in a ListView begins at zero.). To get a list of the selected items,
call getSelectedItems( ) on the selection model. It isshown here:

ObservableList<T> getSelectedItems( )

It returns an ObservableList of the items. Because ObservableList extends java.util.List,you


can access the items in the list just as you would any other List collection.To experiment with
multiple selections, you can modify the preceding program asfollows. First, make lvTransport
final so it can be accessed within the change event handler.Next, add this line:

lvTransport.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
It enables multiple-selection mode for lvTransport. Finally, replace the change eventhandler
with the one shown here:
lvSelModel.selectedItemProperty().addListener(
new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> changed,String oldVal, String newVal)
{
String selItems = "";
ObservableList<String> selected =
lvTransport.getSelectionModel().getSelectedItems();
// Display the selections.
for(int i=0; i < selected.size(); i++)
selItems += "\n " + selected.get(i);
response.setText("All transports selected: " + selItems);
}
});
After making these changes, the program will display all selected forms of transports, as
the following output shows:

You might also like