Johan Vos, Stephen Chin, Weiqi Gao, James Weaver, Dean Iverson Pro Java FX 9 A Definitiv

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 356

DownloadJohan Vos, Stephen Chin, Weiqi Gao, James Weaver, Dean Iverson - Pro Java FX 9 A Definitiv
Open PDF In BrowserView PDF
Pro JavaFX 9
A Definitive Guide to Building Desktop,
Mobile, and Embedded Java Clients
—
Fourth Edition
—
Johan Vos
Stephen Chin
Weiqi Gao
James Weaver
Dean Iverson

Pro JavaFX 9
A Definitive Guide to Building Desktop,
Mobile, and Embedded Java Clients
Fourth Edition

Johan Vos
Stephen Chin
Weiqi Gao
James Weaver
Dean Iverson

Pro JavaFX 9: A Definitive Guide to Building Desktop, Mobile, and Embedded Java Clients
Johan Vos					Stephen Chin
Leuven, Belgium					BELMONT, California, USA
Weiqi Gao					James Weaver
Ballwin, Missouri, USA				
Marion, Indiana, USA
Dean Iverson					
Fort Collins, Colorado, USA			
ISBN-13 (pbk): 978-1-4842-3041-1			
https://doi.org/10.1007/978-1-4842-3042-8

ISBN-13 (electronic): 978-1-4842-3042-8

Library of Congress Control Number: 2017963349
Copyright © 2018 by Johan Vos, Stephen Chin, Weiqi Gao, James Weaver, and Dean Iverson
This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the
material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation,
broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage
and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or
hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with
every occurrence of a trademarked name, logo, or image we use the names, logos, and images only in an
editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are
not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to
proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of publication,
neither the authors nor the editors nor the publisher can accept any legal responsibility for any errors or
omissions that may be made. The publisher makes no warranty, express or implied, with respect to the material
contained herein.
Cover image by Freepik (www.freepik.com)
Managing Director: Welmoed Spahr
Editorial Director: Todd Green
Acquisitions Editor: Steve Anglin
Development Editor: Matthew Moodie
Technical Reviewers: Mark Heckler and Jonathan Giles
Coordinating Editor: Mark Powers
Copy Editor: Kimberly Burton-Weisman
Distributed to the book trade worldwide by Springer Science+Business Media New York,
233 Spring Street, 6th Floor, New York, NY 10013. Phone 1-800-SPRINGER, fax (201) 348-4505, e-mail
orders-ny@springer-sbm.com, or visit www.springeronline.com. Apress Media, LLC is a California LLC
and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance Inc).
SSBM Finance Inc is a Delaware corporation.
For information on translations, please e-mail rights@apress.com, or visit http://www.apress.com/
rights-permissions.
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions
and licenses are also available for most titles. For more information, reference our Print and eBook Bulk
Sales web page at http://www.apress.com/bulk-sales.
Any source code or other supplementary material referenced by the author in this book is available to
readers on GitHub via the book's product page, located at www.apress.com/9781484230411. For more
detailed information, please visit http://www.apress.com/source-code.
Printed on acid-free paper

Contents
About the Authors���������������������������������������������������������������������������������������������������� ix
About the Technical Reviewers������������������������������������������������������������������������������� xi
Acknowledgments������������������������������������������������������������������������������������������������� xiii
Foreword�����������������������������������������������������������������������������������������������������������������xv
Introduction�����������������������������������������������������������������������������������������������������������xvii
■Chapter
■
1: Getting a Jump-Start in JavaFX����������������������������������������������������������� 1
A Brief History of JavaFX�������������������������������������������������������������������������������������������������� 1
Prepare Your JavaFX Journey������������������������������������������������������������������������������������������� 4
Required Tools���������������������������������������������������������������������������������������������������������������������������������������� 4
JavaFX, the Community�������������������������������������������������������������������������������������������������������������������������� 5
Use the Official Specifications���������������������������������������������������������������������������������������������������������������� 5
Scenic View�������������������������������������������������������������������������������������������������������������������������������������������� 6

Developing Your First JavaFX Program: Hello Earthrise��������������������������������������������������� 7
Compiling and Running from the Command Line����������������������������������������������������������������������������������� 8
Understanding the Hello Earthrise Program������������������������������������������������������������������������������������������� 9
Building and Running the Program with NetBeans������������������������������������������������������������������������������ 16

Developing Your Second JavaFX Program: “More Cowbell!”������������������������������������������ 20
Building and Running the Audio Configuration Program���������������������������������������������������������������������� 20
The Behavior of the Audio Configuration Program������������������������������������������������������������������������������� 21
Understanding the Audio Configuration Program��������������������������������������������������������������������������������� 22
Colors and Gradients���������������������������������������������������������������������������������������������������������������������������� 25
The Model Class for the Audio Configuration Example������������������������������������������������������������������������� 26
Using InvalidationListeners and Lambda Expressions�������������������������������������������������������������������������� 27
iii

■ Contents

Surveying JavaFX Features�������������������������������������������������������������������������������������������� 29
Summary������������������������������������������������������������������������������������������������������������������������ 30
Resources���������������������������������������������������������������������������������������������������������������������� 31
■Chapter
■
2: Creating a User Interface in JavaFX��������������������������������������������������� 33
Programmatic vs. Declarative Creation of the User Interface���������������������������������������� 33
Introduction to Node-Centric UIs������������������������������������������������������������������������������������ 33
Setting the Stage������������������������������������������������������������������������������������������������������������ 34
Understanding the Stage Class������������������������������������������������������������������������������������������������������������ 34
Using the Stage Class: The StageCoach Example�������������������������������������������������������������������������������� 35
Understanding the StageCoach Program��������������������������������������������������������������������������������������������� 39

Making a Scene�������������������������������������������������������������������������������������������������������������� 46
Using the Scene Class: The OnTheScene Example������������������������������������������������������������������������������� 46
Understanding the OnTheScene Program�������������������������������������������������������������������������������������������� 48

Handling Input Events���������������������������������������������������������������������������������������������������� 56
Surveying Mouse, Keyboard, Touch, and Gesture Events and
Handlers����������������������������������������������������������������������������������������������������������������������������������������������� 56
Understanding the KeyEvent Class������������������������������������������������������������������������������������������������������� 57
Understanding the MouseEvent Class�������������������������������������������������������������������������������������������������� 57
Understanding the TouchEvent Class��������������������������������������������������������������������������������������������������� 58
Understanding the GestureEvent Class������������������������������������������������������������������������������������������������ 58

Animating Nodes in the Scene��������������������������������������������������������������������������������������� 58
Using a Timeline for Animation������������������������������������������������������������������������������������������������������������� 59
Using the Transition Classes for Animation������������������������������������������������������������������������������������������ 64
The Zen of Node Collision Detection����������������������������������������������������������������������������������������������������� 71

Summary������������������������������������������������������������������������������������������������������������������������ 81
Resources���������������������������������������������������������������������������������������������������������������������� 81
■Chapter
■
3: Properties and Bindings��������������������������������������������������������������������� 83
Forerunners of JavaFX Binding�������������������������������������������������������������������������������������� 83
A Motivating Example����������������������������������������������������������������������������������������������������� 84

iv

■ Contents

Understanding Key Interfaces and Concepts����������������������������������������������������������������� 87
Understanding the Observable Interface���������������������������������������������������������������������������������������������� 88
Understanding the ObservableValue Interface������������������������������������������������������������������������������������� 89
Understanding the WritableValue Interface������������������������������������������������������������������������������������������ 90
Understanding the ReadOnlyProperty Interface����������������������������������������������������������������������������������� 90
Understanding the Property Interface�������������������������������������������������������������������������������������������������� 90
Understanding the Binding Interface���������������������������������������������������������������������������������������������������� 92

Type-Specific Specializations of Key Interfaces������������������������������������������������������������� 93
A Common Theme for Type-Specific Interfaces������������������������������������������������������������������������������������ 94
Commonly Used Classes���������������������������������������������������������������������������������������������������������������������� 97

Creating Bindings����������������������������������������������������������������������������������������������������������� 98
Understanding the Bindings Utility Class���������������������������������������������������������������������������������������������� 98
Understanding the Fluent Interface API���������������������������������������������������������������������������������������������� 102

Understanding the JavaFX Beans Convention�������������������������������������������������������������� 112
The JavaFX Beans Specification��������������������������������������������������������������������������������������������������������� 113
Understanding the Eagerly Instantiated Properties Strategy�������������������������������������������������������������� 113
Understanding the Lazily Instantiated Properties Strategy���������������������������������������������������������������� 117
Using Selection Bindings�������������������������������������������������������������������������������������������������������������������� 119

Adapting JavaBeans Properties to JavaFX Properties������������������������������������������������� 122
Understanding JavaBeans Properties������������������������������������������������������������������������������������������������� 122
Understanding the JavaFX Property Adapters������������������������������������������������������������������������������������ 124

Summary���������������������������������������������������������������������������������������������������������������������� 127
Resources�������������������������������������������������������������������������������������������������������������������� 128
■Chapter
■
4: Using Scene Builder to Create a User Interface������������������������������� 129
Setting the Stage with FXML���������������������������������������������������������������������������������������� 130
Creating a User Interface Graphically with JavaFX Scene Builder����������������������������������������������������� 130
Understanding the FXML File�������������������������������������������������������������������������������������������������������������� 131
Understanding the Controller�������������������������������������������������������������������������������������������������������������� 135
Understanding the FXMLLoader��������������������������������������������������������������������������������������������������������� 138

v

■ Contents

Understanding the FXML Loading Facility�������������������������������������������������������������������� 140
Understanding the FXMLLoader Class������������������������������������������������������������������������������������������������ 141
Understanding the @FXML Annotation����������������������������������������������������������������������������������������������� 146

Exploring the Capabilities of FXML Files���������������������������������������������������������������������� 149
The Deserialization Power of the FXML Format���������������������������������������������������������������������������������� 149
Understanding Default and Static Properties������������������������������������������������������������������������������������� 156
Understanding Attribute Resolutions and Bindings���������������������������������������������������������������������������� 156
Using Multiple FXML Files������������������������������������������������������������������������������������������������������������������ 161
Creating Custom Components Using fx:root��������������������������������������������������������������������������������������� 168
Event Handling Using Scripting or Controller Properties�������������������������������������������������������������������� 173

Using JavaFX Scene Builder����������������������������������������������������������������������������������������� 175
Overview of JavaFX Scene Builder����������������������������������������������������������������������������������������������������� 176
Understanding the Menu Bar and Items��������������������������������������������������������������������������������������������� 176
Understanding the Library Panel�������������������������������������������������������������������������������������������������������� 182
Understanding the Document Panel��������������������������������������������������������������������������������������������������� 185
Understanding the Content Panel������������������������������������������������������������������������������������������������������� 187
Understanding the Inspector Panel���������������������������������������������������������������������������������������������������� 188

Summary���������������������������������������������������������������������������������������������������������������������� 191
Resources�������������������������������������������������������������������������������������������������������������������� 191
■Chapter
■
5: Collections and Concurrency����������������������������������������������������������� 193
Understanding Observable Collections and Arrays������������������������������������������������������ 193
Understanding ObservableList������������������������������������������������������������������������������������������������������������ 194
Handling Change Events in ListChangeListener��������������������������������������������������������������������������������� 198
Understanding ObservableMap���������������������������������������������������������������������������������������������������������� 202
Understanding ObservableSet������������������������������������������������������������������������������������������������������������ 206
Understanding ObservableArrays������������������������������������������������������������������������������������������������������� 209
Using Factory and Utility Methods from FXCollections����������������������������������������������������������������������� 213

Using the JavaFX Concurrency Framework������������������������������������������������������������������ 218
Identifying the Threads in a JavaFX Application��������������������������������������������������������������������������������� 218
Fixing Unresponsive UIs��������������������������������������������������������������������������������������������������������������������� 224
Understanding the javafx.concurrent Framework������������������������������������������������������������������������������ 230
vi

■ Contents

Mixing JavaFX with Other GUI Toolkits������������������������������������������������������������������������� 251
Embedding JavaFX Scenes in Swing Applications����������������������������������������������������������������������������� 251
Embedding JavaFX Scenes in SWT Applications�������������������������������������������������������������������������������� 258
Embedding Swing Components in JavaFX Applications��������������������������������������������������������������������� 267

Summary���������������������������������������������������������������������������������������������������������������������� 273
Resources�������������������������������������������������������������������������������������������������������������������� 275
■Chapter
■
6: Creating Charts in JavaFX���������������������������������������������������������������� 277
Structure of the JavaFX Chart API�������������������������������������������������������������������������������� 277
Using the JavaFX PieChart������������������������������������������������������������������������������������������� 279
The Simple Example��������������������������������������������������������������������������������������������������������������������������� 279
Some Modifications���������������������������������������������������������������������������������������������������������������������������� 282

Using the XYChart��������������������������������������������������������������������������������������������������������� 287
Using the ScatterChart����������������������������������������������������������������������������������������������������������������������� 288
Using the LineChart���������������������������������������������������������������������������������������������������������������������������� 294
Using the BarChart����������������������������������������������������������������������������������������������������������������������������� 295
Using the StackedBarChart����������������������������������������������������������������������������������������������������������������� 297
Using the AreaChart���������������������������������������������������������������������������������������������������������������������������� 299
Using the StackedAreaChart��������������������������������������������������������������������������������������������������������������� 300
Using the BubbleChart������������������������������������������������������������������������������������������������������������������������ 300

Summary���������������������������������������������������������������������������������������������������������������������� 305
Resources�������������������������������������������������������������������������������������������������������������������� 305
■Chapter
■
7: Connecting to Enterprise Services��������������������������������������������������� 307
Front-End and Back-End Platforms������������������������������������������������������������������������������ 307
Merging JavaFX and Java Enterprise Modules in the Same Environment������������������� 309
Using JavaFX to Call Remote (Web) Services��������������������������������������������������������������� 310
REST��������������������������������������������������������������������������������������������������������������������������������������������������� 310
Using External Libraries���������������������������������������������������������������������������������������������������������������������� 336

Summary���������������������������������������������������������������������������������������������������������������������� 341
Index��������������������������������������������������������������������������������������������������������������������� 343
vii

About the Authors
Johan Vos is a Java Champion who started to work with Java in 1995. As part of the Blackdown team, he
helped port Java to Linux. With LodgON, the company he cofounded, he has been mainly working on
Java-based solutions for social networking software. His main focus is on end-to-end Java, combining the
strengths of back-end systems and embedded devices. His favorite technologies are currently Java EE/
Glassfish at the back end and JavaFX at the front end. He contributes to a number of open source projects,
including DataFX and the Android port of JavaFX. Johan’s blog can be followed at http://blogs.lodgon.
com/johan, he tweets at http://twitter.com/johanvos, and can be reached at johan@lodgon.com.
Stephen Chin is a Java Ambassador at Oracle specializing in embedded and user interface technology and
the JavaOne Content Chair. He has been featured at Java conferences around the world including Devoxx,
JFokus, OSCON, JFall, GeeCON, JustJava, and JavaOne, where he three times received a Rock Star Award.
Stephen is an avid motorcyclist who has done several Pan-European evangelism tours, interviewing hackers
in their natural habitat and posting the videos on http://nighthacking.com. When he is not traveling, he
enjoys teaching kids how to do embedded and robot programming together with his 11-year-old daughter.
Weiqi Gao is a principal software engineer with Object Computing, Inc. in St. Louis, Missouri. He has
decades of software development experience and has been using Java technology since 1998. He is
interested in programming languages, object-oriented systems, distributed computing, and graphical user
interfaces. He is a member of the steering committee of the St. Louis Java Users Group. Weiqi holds a PhD in
mathematics.
James Weaver is an author, a speaker, a teacher, and a developer in rich Internet application technologies
such as JavaFX. He is also an Oracle engineer. He may be contacted at jim.weaver@javafxpert.com.
Dean Iverson has been writing software professionally for more than 15 years. He is employed by the
Virginia Tech Transportation Institute, where he is a senior researcher and rich client application developer.
He also has a small software consultancy called Pleasing Software Solutions, which he cofounded with
his wife.

ix

About the Technical Reviewers
Mark Heckler is a Java software architect/engineer with development experience in numerous
environments. He has worked for and with key players in the manufacturing, emerging markets, retail,
medical, telecom, and financial industries to develop and deliver critical capabilities on time and on
budget. Currently, he works primarily with enterprise customers using Java throughout the stack. He also
participates in open source development at every opportunity, being a JFXtras project committer, developer
of DialogFX and MonologFX, co-developer of Autonomous4j, and more. When Mark isn’t working with Java,
he enjoys sharing his experiences at conferences and via the Java Jungle web site (https://blogs.oracle.
com/javajungle), his personal web site (www.thehecklers.org), and Twitter (@MkHeck). Mark lives with his
very understanding wife, three kids, and dog in St. Louis, Missouri.
Jonathan Giles is a software engineer who has worked with Java and JavaFX for a very long time. He has
been responsible for large sections of the JavaFX toolkit stack since 2009, and as such, is intimately familiar
with it. He is a JavaOne Rockstar speaker and track lead, an initiator of many open source projects (such as
ControlsFX and Scenic View—both referenced in this book), and a technical reviewer of many Java-related
books. He blogs at jonathangiles.net, and can be found on Twitter @JonathanGiles.

xi

Acknowledgments
Writing a book is often done in spare time. I want to thank my wife, Kathleen, and our children, Merlijn
and Linde, for allowing me to spend evening and weekend time in front of my computer. I want to thank
authors Jim Weaver, Weiqi Gao, Stephen Chin, and Dean Iverson; technical reviewer Mark Heckler; and
the Apress team for their trust in me. A special thanks to my LodgON colleagues Joeri Sykora and Erwin
Morrhey for helping me with the examples. The JavaFX team at Oracle did a great job releasing JavaFX 8.
The combination of their efforts and those of the Java community makes JavaFX an excellent platform for an
increasing number of clients.

—Johan Vos
To my wife, Justine, and daughters, Cassandra and Priscilla, who supported me in writing this book on top of
all my other responsibilities. Also, a huge thanks to the entire author team, including our newest members,
Johan Vos and Mark Heckler, who both went above and beyond in their contributions to this title. Finally, a
great debt of gratitude to the JavaFX team and JVM language designers who have produced technology that
will profoundly change the way we design and code user interfaces going forward.

—Stephen Chin
I would like to thank my wife, Youhong Gong, for her support, understanding, and encouragement during
the writing process. My thanks also go to the author and technical review team: Johan Vos, Jim Weaver,
Stephen Chin, Dean Iverson, and Mark Heckler for making this book a fun project. I share with my coauthors
the appreciation for the JavaFX team at Oracle and the editorial team at Apress.

—Weiqi Gao
I would like to thank my family, Sondra, Alex, and Matt, for their support and understanding during yet
another writing project. You guys make this possible. I would also like to thank the writing and review
team of Jim Weaver, Stephen Chin, Weiqi Gao, Johan Vos, and Mark Heckler for their dedication and their
patience. The editorial team at Apress was, as usual, first rate and utterly professional. And, of course, none
of this would be possible without the hard work of an extremely talented team of engineers on the JavaFX
team at Oracle.

—Dean Iverson
To my wife Julie, daughters, Lori and Kelli, son, Marty, and grandchildren, Kaleb and Jillian. Thanks Merrill
and Barbara Bishir, Ken and Marilyn Prater, and Walter Weaver for being such wonderful examples. My
contributions to this book are dedicated to the memory of Merrill Bishir and Ken Prater. “I have told you
these things, so that in me you may have peace. In this world you will have trouble. But take heart! I have
overcome the world.” (John 16:33)

—James Weaver

xiii

Foreword
I remember it distinctly, like it was yesterday: standing center stage at Moscone Center when we launched
JavaFX at JavaOne 2007. We promised to build a world-class client platform for Java. With the world watching
with skeptical eyes and in a crowded client arena, we set out to build the dream. In hindsight, it was a rather
ambitious goal.
Fast-forward seven years, with the release of Java SE 8, we have taken a huge leap forward in fulfilling
that promise. As the vision unfolded, our product plans have shifted to match the evolving RIA market and
what developers and the Java community told us they were looking for. As someone who was there at the
inception of JavaFX and who has watched it mature over the last seven years to this current release, my
feelings are akin to a parent watching a toddler blossom.
James Weaver and Stephen Chin have been traveling through the evolution of JavaFX with me. They
have both presented on JavaFX at numerous international conferences and have been developing with
and blogging about JavaFX since 2007. James is a 30-year software veteran who has authored several books
on Java, as well as articles for Java Magazine and the Oracle Technology Network. He has also developed
numerous JavaFX applications for a wide variety of customers.
Stephen is passionate about open source technologies and is the founder of WidgetFX and JFXtras.
He also has a deep passion for improving development technologies and processes, as well as agile
development methodologies.
Johan Vos is cofounder of LodgON. He holds a PhD in applied physics and he has been a very prolific
member of the JavaFX community. His interest lies in the enterprise communication aspects of JavaFX,
combining the world of large servers with end-user devices. Johan’s analogy to physics: The grand unified
theory combines quantum mechanics (small) with relativity theory (large); similarly, in software, Java
combines JavaFX with Java EE.
Dean Iverson is a longtime client developer with a great eye for creating elegant user interfaces.
He develops GroovyFX libraries and is a contributor to the JFXtras project. He has been developing and
blogging about JavaFX since 2007. Weiqi Gao holds a PhD in mathematics. His expertise is in the language
aspects of JavaFX, as reflected in the chapters on properties and bindings, and collections and concurrency.
Today, the core JavaFX team at Oracle still has several of the developers who were part of the early
versions of JavaFX and we also have new engineers who have joined us. As we move ahead and open source
JavaFX, we are looking forward to having more developers and experts from the extended Java community
join us in making JavaFX the number one choice for client development.
I am proud and honored to be part of this key software technology. Given their length of experience and
depth of expertise in all aspects of JavaFX and across the Java platform, I cannot think of a better group of
authors to bring you JavaFX 8. I hope you will enjoy this book and find JavaFX as satisfying as I have found it
over the years. I hope it piques your interest sufficiently to join the JavaFX community in making JavaFX the
platform of choice for clients.

—Nandini Ramani
Vice President, Java Client Development
Oracle Corporation

xv

Introduction
As a developer, author, speaker, and advocate for JavaFX since its inception in 2007, I am very excited about
JavaFX 8. It was released in March 2014 as an integral part of Java SE 8, and is the successor to Java Swing. As
you’ll read in the pages of this book, JavaFX runs on desktops (Mac, Windows, Linux), as well as embedded
devices such as the Raspberry Pi. As the Internet of things (IoT) is increasingly realized, JavaFX is well
positioned to enable the user interface of IoT. Also, because of community projects led by folks such as Johan
Vos and Niklas Therning, developers are deploying JavaFX apps on Android and iOS devices.
The JavaFX community has many talented, passionate, and cordial developers, and I count it a privilege
to call them my colleagues. One such colleague, Johan Vos, is a coauthor of our Pro JavaFX 2 book, and is the
lead author of Pro JavaFX 8. It has been my pleasure to continue working with Johan on this book under his
leadership. Please join me in welcoming and congratulating him in this role, perhaps by tweeting him at
@JohanVos or posting a review of this book on Amazon. It is my hope that you’ll find this book both enjoyable
and instrumental in helping you learn JavaFX!

—James L. Weaver
Java Technology Ambassador
Oracle Corporation

xvii

CHAPTER 1

Getting a Jump-Start in JavaFX
Don’t ask what the world needs. Ask what makes you come alive, and go do it. Because
what the world needs is people who have come alive.
—Howard Thurman
At the annual JavaOne conference in May 2007, Sun Microsystems announced a new product family named
JavaFX. Its stated purpose includes enabling the development and deployment of content-rich applications
on consumer devices such as cell phones, televisions, in-dash car systems, and browsers. Josh Marinacci,
a software engineer at Sun, made the following statement, very appropriately, in a Java Posse interview:
“JavaFX is sort of a code word for reinventing client Java and fixing the sins of the past.” He was referring
to the fact that Java Swing and Java 2D have lots of capability, but are also very complex. Furthermore,
technologies have evolved a lot since Swing and Java 2D were created. Today’s client systems (desktops as
well as mobile and embedded devices) are equipped with powerful graphical processors—the GPU. JavaFX
takes advantage of the new features and performance increases offered by GPUs. By using FXML, JavaFX
allows us to simply and elegantly express user interfaces (UIs) with a declarative programming style. It also
leverages the full power of Java, because you can instantiate and use the millions of Java classes that exist
today. Add features such as binding the UI to properties in a model and change listeners that reduce the
need for setter methods, and you have a combination that will help restore Java to the client-side Internet
applications.
In this chapter, we give you a jump-start in developing JavaFX applications. After bringing you up to
date on the brief history of JavaFX, we show you how to get the required tools. We also explore some great
JavaFX resources and walk you through the process of compiling and running JavaFX applications. In the
process, you’ll learn a lot about the JavaFX application programming interface (API) as we walk through
application code together.

A Brief History of JavaFX
JavaFX started life as the brainchild of Chris Oliver when he worked for a company named SeeBeyond.
They had a need for richer user interfaces, so Chris created a language that he dubbed F3 (Form Follows
Function) for that purpose. In the article “Mind-Bendingly Cool Innovation” (cited in the “Resources”
section at the end of this chapter), Chris is quoted as follows: “When it comes to integrating people into
business processes, you need graphical user interfaces for them to interact with, so there was a use case for
graphics in the enterprise application space, and there was an interest at SeeBeyond in having richer user
interfaces.”
SeeBeyond was acquired by Sun, who subsequently changed the name of F3 to JavaFX, and announced
it at JavaOne 2007. Chris Oliver joined Sun during the acquisition and continued to lead the development of
JavaFX.

© Johan Vos, Stephen Chin, Weiqi Gao, James Weaver, and Dean Iverson 2018
J. Vos et al., Pro JavaFX 9, https://doi.org/10.1007/978-1-4842-3042-8_1

1

Chapter 1 ■ Getting a Jump-Start in JavaFX

The first version of JavaFX Script was an interpreted language, and was considered a prototype of the
compiled JavaFX Script language that was to come later. Interpreted JavaFX Script was very robust, and
there were two JavaFX books published in the latter part of 2007 based on that version. One was written in
Japanese, and the other was written in English (JavaFX Script: Dynamic Java Scripting for Rich Internet/
Client-Side Applications by Jim Weaver (Apress, 2007)).
While developers were experimenting with JavaFX and providing feedback for improvement, the JavaFX
Script compiler team at Sun was busy creating a compiled version of the language. This included a new set
of runtime API libraries. The JavaFX Script compiler project reached a tipping point in early December 2007,
which was commemorated in a blog post entitled “Congratulations to the JavaFX Script Compiler Team—
The Elephant Is Through the Door.” That phrase came from the JavaFX Script compiler project leader Tom
Ball in a blog post, which contained the following excerpt.

An elephant analogy came to me when I was recently grilled about exactly when the
JavaFX Script compiler team will deliver our first milestone release. “I can’t give you an
accurate date,” I said. “It’s like pushing an elephant through a door; until a critical mass
makes it past the threshold you just don’t know when you’ll be finished. Once you pass that
threshold, though, the rest happens quickly and in a manner that can be more accurately
predicted.”
A screenshot of the silly, compiled JavaFX application written by one of the authors, Jim Weaver, for that
post is shown in Figure 1-1, demonstrating that the project had in fact reached the critical mass to which
Tom Ball referred.

Figure 1-1. Screenshot for the “Elephant Is Through the Door” program
Much progress continued to be made on JavaFX in 2008:

2

•

The NetBeans JavaFX plug-in became available for the compiled version in
March 2008.

•

Many of the JavaFX runtime libraries (mostly focusing on the UI aspects of JavaFX)
were rewritten by a team that included some very talented developers from the
Java Swing team.

Chapter 1 ■ Getting a Jump-Start in JavaFX

•

In July 2008, the JavaFX Preview Software Development Kit (SDK) was released, and at
JavaOne 2008, Sun announced that the JavaFX 1.0 SDK would be released in fall 2008.

•

On December 4, 2008, the JavaFX 1.0 SDK was released. This event increased the
adoption rate of JavaFX by developers and IT managers because it represented a
stable codebase.

•

In April 2009, Oracle and Sun announced that Oracle would be acquiring Sun. The
JavaFX 1.2 SDK was released at JavaOne 2009.

•

In January 2010, Oracle completed its acquisition of Sun. The JavaFX 1.3 SDK was
released in April 2010, with JavaFX 1.3.1 being the last of the 1.3 releases.

At JavaOne 2010, JavaFX 2.0 was announced. The JavaFX 2.0 roadmap was published by Oracle and
included items such as the following.
•

Deprecate the JavaFX Script language in favor of using Java and the JavaFX 2.0 API.
This brings JavaFX into the mainstream by making it available to any language
(e.g., Java, Groovy, and JRuby) that runs on the Java Virtual Machine (JVM). As a
consequence, existing developers do not need to learn a new language, but they can
use existing skills and start developing JavaFX applications.

•

Make the compelling features of JavaFX Script, including binding to expressions,
available in the JavaFX 2.0 API.

•

Offer an increasingly rich set of UI components, building on the components already
available in JavaFX 1.3.

•

Provide a Web component for embedding HTML and JavaScript content into JavaFX
applications.

•

Enable JavaFX interoperability with Swing.

•

Rewrite the media stack from the ground up.

JavaFX 2.0 was released at JavaOne 2011, and has enjoyed a greatly increased adoption rate due to the
innovative features articulated previously.
JavaFX 8 marked another important milestone. JavaFX is now an integral part of the Java Platform,
Standard Edition.
•

This is a clear indication that JavaFX is considered mature enough, and that it is the
future of Java on the client.

•

This greatly benefits developers, as they don’t have to download two SDKs and tool
suites.

•

The new technologies in Java 8, in particular the lambda expressions, Stream API,
and default interface methods, are very usable in JavaFX.

•

Many new features have been added, including native 3D support, a printing API,
and some new controls including a datepicker.

•

Since the release of JavaFX 8, the JavaFX platform follows the same version and
release procedures as the Java Platform, Standard Edition. As a consequence, when
Java 9 was released, JavaFX 9 was released as well.

•

The main focus for Java 9 is modularity. The Java Platform, Standard Edition, has
become bigger and bigger, and not all applications require all classes to be available.
By modularizing the Java Platform, it is easier to create subsets of the Java platform

3

Chapter 1 ■ Getting a Jump-Start in JavaFX

that combine a number of modules that are sufficient to run a particular application.
This modularization effort was huge, and it took many years before it was complete.
All parts of the Java Platform, Standard Edition have been refactored into modules,
including the JavaFX 9 Platform APIs.
•

One of the consequences of the modularization is that it is now not allowed anymore
for code to depend on internal APIs of another module. This has far-reaching
consequences. Before JavaFX 9, Controls were often created by implementing
undocumented internal APIs. Those APIs were public, because they were used
internally by other JavaFX classes, in different packages. As a consequence,
developer could use them as well.

•

Since those internal APIs are now in modules that by default do not expose this
functionality, a new approach was needed for developers who want to create
custom controls. Hence, the JavaFX team was not only faced with moving all the
JavaFX public APIs into a number of modules, it also had to provide public APIs for
functionality that was previously accessed via internal APIs.

In Java 9, the JavaFX platform provides the following modules:
•

javafx.base

•

javafx.controls

•

javafx.fxml

•

javafx.graphics

•

javafx.jmx

•

javafx.media

•

javafx.swing

•

javafx.swt

•

javafx.web

•

jdk.packager

•

jdk.packager.services

Now that you’ve had the obligatory history lesson in JavaFX, let’s get one step closer to writing code by
showing you where some examples, tools, and other resources are.

Prepare Your JavaFX Journey
Required Tools
Because JavaFX is part of Java 9, you don’t have to download a separate JavaFX SDK. The whole JavaFX
API and implementation is part of the Java 9 SE SDK that can be downloaded from www.oracle.com/
technetwork/java/javase/downloads/index.html.
This SDK contains everything you need to develop, run, and package JavaFX applications. You can
compile JavaFX applications using command-line tools contained in the Java 9 SE SDK.
Most developers, however, prefer an integrated development environment (IDE) for increased
productivity. By definition, an IDE that supports Java 9 also supports JavaFX 9. Hence, you can use your
favorite IDE and develop JavaFX applications. In this book, we mainly use the NetBeans IDE, but other

4

Chapter 1 ■ Getting a Jump-Start in JavaFX

IDE’s, such as IntelliJ or Eclipse, can be used as well. The NetBeans IDE can be downloaded from
https://netbeans.org/downloads.
Many JavaFX developers, especially those working on user interfaces, prefer a WYSIWYG tool for
creating interfaces. Scene Builder is a stand-alone tool that allows you to design JavaFX interfaces rather
than coding them. We discuss Scene Builder in Chapter 4. Although Scene Builder produces FXML—and
we discuss FXML in Chapter 3 as well—that can be used in any IDE, NetBeans provides a tight integration
with Scene Builder. The Scene Builder tool can be downloaded at http://gluonhq.com/products/scenebuilder/.

JavaFX, the Community
JavaFX is not a closed-source project, developed in a secret bunker. To the contrary, JavaFX is being
developed in an open spirit, with an open source code base, open mailing lists, and an open and active
community sharing knowledge.
The source code is developed in the OpenJFX project, which is a subproject of the OpenJDK project in
which Java SE is being developed. If you want to examine the source code or the architecture, or if you want
to read the technical discussions on the mailing list, have a look at http://openjdk.java.net/projects/
openjfx.
The developer community is very active, both in OpenJFX as well as in application-specific areas. Many
JavaFX developers regularly blog about their JavaFX activities, and many non-Oracle products and projects
related to JavaFX are being created and maintained by this community.
In addition, blogs maintained by JavaFX engineers and developers are great resources for up-tothe-minute technical information on JavaFX. For example, Oracle JavaFX Engineer Jonathan Giles keep
the developer community apprised of the latest JavaFX innovations at http://fxexperience.com. The
“Resources” section at the end of this chapter contains the URLs of the blogs that the authors of this book
use to engage the JavaFX developer community.
Two important characteristics of the JavaFX Community are its own creativity and the desire to share.
There are a number of open-source efforts bringing added value to the JavaFX Platform. Because of good
cooperation between the JavaFX platform engineers and the external JavaFX developers, these open-source
projects fit very well with the official JavaFX platform.
Some of the most interesting efforts are listed here:
•

Gluon allows you to create iOS and Android applications using Java and JavaFX. As
a consequence, your JavaFX application can be used to create an app for Android
devices and for the iPhone or the iPad.

This mobile port of JavaFX is discussed in more detail in Chapter 12.
•

ControlsFX is a project working on adding high-quality controls and add-ons to the
JavaFX platform.

•

JFXtras.org is another project working on adding high-quality controls and add-ons
to the JavaFX platform.

It is worth mentioning that the JavaFX team is closely watching the efforts in both JFXtras.org and
ControlsFX, and ideas that start in one of those projects might make it into one of the next releases of JavaFX.
Take a few minutes to explore these sites. Next, we point out some valuable resources.

Use the Official Specifications
While developing JavaFX applications, it is very useful to have access to the API Javadoc documentation,
which is available at http://download.java.net/jdk9/jfxdocs/index.html and shown in Figure 1-2.

5

Chapter 1 ■ Getting a Jump-Start in JavaFX

Figure 1-2. JavaFX SDK API Javadoc
The API documentation in Figure 1-2, for example, shows how to use the Rectangle class, located in the
javafx.scene.shape package. Scrolling down this web page shows the properties, constructors, methods,
and other helpful information about the Rectangle class. By the way, this API documentation is available in
the Java 8 SE SDK that you downloaded, but we wanted you to know how to find it online as well.
Apart from the Javadoc, it is very useful to have the Cascading Style Sheets (CSS) style reference at hand as
well. This document explains all the style classes that can be applied to a particular JavaFX element. You can
find this document at http://download.java.net/jdk9/jfxdocs/javafx/scene/doc-files/cssref.html.

Scenic View
You already downloaded Scene Builder, which is the tool that allows you to create UIs by designing them,
rather than writing code. We expect that there will be more tools developed by companies and individuals
that help you create JavaFX applications. One of the first tools that was made available for free and that is
very helpful when debugging JavaFX applications is ScenicView, originally created by Amy Fowler at Oracle,
and later maintained by Jonathan Giles. You can download ScenicView at http://scenic-view.org/.
ScenicView is particularly helpful because it provides a convenient UI that allows developers to inspect
properties of nodes (i.e., dimensions, translations, CSS) at runtime.

Packaging and Distribution
The techniques used for delivering software to the end user are always changing. In the past, the preferred
way for delivering Java applications was via the Java Network Launch Protocol (JNLP). Doing so, both
applets and stand-alone applications can be installed on a client. However, there are a number of issues

6

Chapter 1 ■ Getting a Jump-Start in JavaFX

with this technique. The idea only works if the end user has a JVM installed that is capable of executing the
application. This is not always true. Even in the desktop world, where a system can be delivered preinstalled
with a JVM, there are issues with versioning and security. Indeed, some applications are hard-coded against
a specific version of the JVM. Although vulnerabilities in the JVM are in most cases fixed very fast, this still
requires the end user to always install the latest version of the JVM, which can be pretty frustrating.
On top of that, browser manufacturers are increasingly reluctant to support alternative embedded
platforms. In summary, relying on a browser and on a local, preinstalled JVM does not provide the best enduser experience.
The client software industry is shifting more and more toward the so-called app stores. In this concept,
applications can be downloaded and installed that are self-containing. They do not rely on preinstalled
execution environments. The principles originated in the mobile space, where Apple’s AppStore and
Android’s Play Store are leading the market. Especially in these markets, single-click installs have a huge
advantage over local downloads, unpacking, manual configuration, and more nightmares.
In Java terminology, a self-contained application means that the application is bundled together with a
JVM that is capable of running the application. In the past, this idea was often rejected because it made the
application bundle too big. However, with increasing memory and storage capacities, and with decreasing
costs of sending bytes over the Internet, this disadvantage is becoming less relevant.
There are a number of technologies being developed currently that help you bundle your application
with the correct JVM version and package it.
The standard technology for bundling Java applications with a Java Virtual Machine runtime is the
JavaPackager, which is developed inside the OpenJFX project area. JavaFXPackager contains an API for
creating self-contained bundles. This tool is used by NetBeans, and it can be used to generate self-contained
bundles with just a few clicks.
Now that you have the tools installed, we show you how to create a simple JavaFX program, and then
we walk through it in detail. The first program that we’ve chosen for you is called “Hello Earthrise,” which
demonstrates more features than the typical beginning “Hello World” program.

Developing Your First JavaFX Program: Hello Earthrise
On Christmas Eve in 1968, the crew of Apollo 8 entered lunar orbit for the first time in history. They were
the first humans to witness an “Earthrise,” taking the magnificent picture shown in Figure 1-3. This image is
dynamically loaded from this book’s web site when the program starts, so you’ll need to be connected to the
Internet to view it.

7

Chapter 1 ■ Getting a Jump-Start in JavaFX

Figure 1-3. The Hello Earthrise program
In addition to demonstrating how to dynamically load images over the Internet, this example shows you
how to use animation in JavaFX. Now it’s time for you to compile and run the program. We show you two
ways to do this: from the command line and using NetBeans.

Compiling and Running from the Command Line
We usually use an IDE to build and run JavaFX programs, but to take all of the mystery out of the process we
use the command-line tools first.

■■Note For this exercise, as with most others in the book, you need the source code. If you prefer not to type
the source code into a text editor, you can obtain the source code for all of the examples in this book from the
code download site. See the “Resources” section at the end of this chapter for the location of this site.
Assuming that you’ve downloaded and extracted the source code for this book into a directory, follow
the directions in this exercise, performing all of the steps as instructed. We dissect the source code after the
exercise.

8

Chapter 1 ■ Getting a Jump-Start in JavaFX

COMPILING AND RUNNING THE HELLO EARTHRISE PROGRAM FROM
THE COMMAND LINE
You’ll use the javac and java command-line tools to compile and run the program in this exercise. From
the command-line prompt on your machine:
1.

Navigate to the Chapter01/Hello directory.

2.

Execute the following command to compile the HelloEarthRiseMain.java file.
javac -d . HelloEarthRiseMain.java

3.

Because the –d option was used in this command, the class files generated are
placed in directories matching the package statements in the source files. The roots
of those directories are specified by the argument given for the –d option, in this
case the current directory.

4.

To run the program, execute the following command. Note that we use the fully
qualified name of the class that will be executed, which entails specifying the
nodes of the path name and the name of the class, all separated by periods.
java projavafx.helloearthrise.ui.HelloEarthRiseMain

The program should appear as shown in Figure 1-4, with the text scrolling slowly upward, reminiscent
of the Star Wars opening crawls.
Congratulations on completing your first exercise as you explore JavaFX!

Understanding the Hello Earthrise Program
Now that you’ve run the application, let’s walk through the program listing together. The code for the Hello
Earthrise application is shown in Listing 1-1.
Listing 1-1. The HelloEarthRiseMain.java Program
package projavafx.helloearthrise.ui;
import
import
import
import
import
import
import
import
import
import
import
import
import
import

javafx.animation.Interpolator;
javafx.animation.Timeline;
javafx.animation.TranslateTransition;
javafx.application.Application;
javafx.geometry.VPos;
javafx.scene.Group;
javafx.scene.Scene;
javafx.scene.image.Image;
javafx.scene.image.ImageView;
javafx.scene.paint.Color;
javafx.scene.shape.Rectangle;
javafx.scene.text.Font;
javafx.scene.text.FontWeight;
javafx.scene.text.Text;

9

Chapter 1 ■ Getting a Jump-Start in JavaFX

import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* Main class for the "Hello World" style example
*/
public class HelloEarthRiseMain extends Application {
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        String message
                = "Earthrise at Christmas: "
                + "[Forty] years ago this Christmas, a turbulent world "
                + "looked to the heavens for a unique view of our home "
                + "planet. This photo of Earthrise over the lunar horizon "
                + "was taken by the Apollo 8 crew in December 1968, showing "
                + "Earth for the first time as it appears from deep space. "
                + "Astronauts Frank Borman, Jim Lovell and William Anders "
                + "had become the first humans to leave Earth orbit, "
                + "entering lunar orbit on Christmas Eve. In a historic live "
                + "broadcast that night, the crew took turns reading from "
                + "the Book of Genesis, closing with a holiday wish from "
                + "Commander Borman: \"We close with good night, good luck, "
                + "a Merry Christmas, and God bless all of you -- all of "
                + "you on the good Earth.\"";
        // Reference to the Text
        Text textRef = new Text(message);
        textRef.setLayoutY(100);
        textRef.setTextOrigin(VPos.TOP);
        textRef.setTextAlignment(TextAlignment.JUSTIFY);
        textRef.setWrappingWidth(400);
        textRef.setFill(Color.rgb(187, 195, 107));
        textRef.setFont(Font.font("SansSerif", FontWeight.BOLD, 24));
        // Provides the animated scrolling behavior for the text
        TranslateTransition transTransition = new TranslateTransition(new Duration(75000),
textRef);
        transTransition.setToY(-820);
        transTransition.setInterpolator(Interpolator.LINEAR);
        transTransition.setCycleCount(Timeline.INDEFINITE);

10

Chapter 1 ■ Getting a Jump-Start in JavaFX

        // Create an ImageView containing the Image
        Image image = new Image ("http://projavafx.com/images/earthrise.jpg");
        ImageView imageView = new ImageView(image);
        // Create a Group containing the text
        Group textGroup = new Group(textRef);
        textGroup.setLayoutX(50);
        textGroup.setLayoutY(180);
        textGroup.setClip(new Rectangle(430, 85));
        // Combine ImageView and Group
        Group root = new Group(imageView, textGroup);
        Scene scene = new Scene(root, 516, 387);
        stage.setScene(scene);
        stage.setTitle("Hello Earthrise");
        stage.show();
        // Start the text animation
        transTransition.play();
    }
}
Now that you’ve seen the code, let’s take a look at its constructs and concepts in some more detail.

What Happened to the Builders?
If you were using JavaFX 2 before, you are probably familiar with the so-called builder pattern. Builders
provide a declarative style of programming. Rather than calling set() methods on a class instance to specify
its fields, the builder pattern uses an instance of a Builder class to define how the target class should be
composed.
Builders were very popular in JavaFX. However, it turned out that there were major technical hurdles
with keeping them in the platform. As a consequence, the decision was made to phase out builders. In Java 8,
Builder classes were still usable, but they are deprecated. In Java 9, Builder classes have been removed
entirely.
More information on the reason why Builder classes are not preferred anymore can be found in a
mailing list entry by JavaFX Client Architect Richard Bair at http://mail.openjdk.java.net/pipermail/
openjfx-dev/2013-March/006725.html. The bottom of this entry contains a very important statement:
“I believe that FXML or lambda’s or alternative languages all provide other avenues for achieving the same
goals as builders but without the additional cost in byte codes or classes.”
This is what we will show throughout this book. Near the end of this chapter, we show a first example
of a lambda expression in our code. In Chapter 3, we show how Scene Builder and FXML allow you to use a
declarative way of defining a UI.
In the current example, we programmatically define the different components of the UI, and we glue
them together. In Chapter 3, we show the same example using a declarative FXML-based approach.

11

Chapter 1 ■ Getting a Jump-Start in JavaFX

The JavaFX Application
Let’s have a look at the class declaration in our first example:
public class HelloEarthRiseMain extends Application
This declaration states that our application extends the javafx.application.Application class. This
class has one abstract method that we should implement:
public void start(Stage stage) {}
This method will be called by the environment that executes our JavaFX application.
Depending on the environment, JavaFX applications will be launched in a different way. As a developer,
you don’t have to worry about how your application is launched, and where the connection to a physical
screen is made. You have to implement the “start” method and use the provided Stage parameter to create
your UI, as discussed in the next paragraph.
In our command-line example, we launched the applications by executing the main method of the
application class. The implementation of the main method is very simple:
public static void main(String[] args) {
    Application.launch(args);
}
The only instruction in this main method is a call to the static launch method of the application, which
will launch the application.

■■Tip

A JavaFX application always has to extend the javafx.application.Application class.

A Stage and a Scene
A Stage contains the UI of a JavaFX app, whether it is deployed on the desktop, on an embedded system,
or on other devices. On the desktop, for example, a Stage has its own top-level window, which typically
includes a border and title bar.
The initial stage is created by the JavaFX runtime, and passed to you via the start() method, as
described in the previous paragraph. The Stage class has a set of properties and methods. Some of these
properties and methods, as shown in the following code snippet from the listing, are as follows.
•

A scene that contains the graphical nodes in the UI

•

A title that appears in the title bar of the window (when deployed on the desktop)

•

The visibility of the Stage

stage.setScene(scene);
stage.setTitle("Hello Earthrise");
stage.show();
A Scene is the top container in the JavaFX scene graph. A Scene holds the graphical elements that are
displayed on the Stage. Every element in a Scene is a graphical node, which is any class that extends
javafx.scene.Node. The scene graph is a hierarchical representation of the Scene. Elements in the scene
graph may contain child elements, and all of them are instances of the Node class.

12

Chapter 1 ■ Getting a Jump-Start in JavaFX

The Scene class contains a number of properties, such as its width and height. A Scene also has a property
named root that holds the graphical elements that are displayed in the Scene, in this case a Group instance that
contains an ImageView instance (which displays an image) and a Group instance. Nested within the latter Group
is a Text instance (which is a graphical element, usually called a graphical node, or simply node).
Notice that the root property of the Scene contains an instance of the Group class. The root property may
contain an instance of any subclass of javafx.scene.Node, and typically contains one capable of holding its
own set of Node instances. Take a look at the JavaFX API documentation that we showed you how to access in
the “Use the Official Specifications” section and check out the Node class to see the properties and methods
available to any graphical node. Also, take a look at the ImageView class in the javafx.scene.image package
and the Group class in the javafx.scene package. In both cases, they inherit from the Node class.

■■Tip We can’t emphasize enough the importance of having the JavaFX API documentation handy
while reading this book. As classes, variables, and functions are mentioned, it’s a good idea to look at the
documentation to get more information. In addition, this habit helps you become more familiar with what is
available to you in the API.

Displaying Images
As shown in the following code, displaying an image entails using an ImageView instance in conjunction
with an Image instance.
Image image = new Image ("http://projavafx.com/images/earthrise.jpg");
ImageView imageView = new ImageView(image);
The Image instance identifies the image resource and loads it from the URL assigned to its URL variable.
Both of these classes are located in the javafx.scene.image package.

Displaying Text
In the example, we created a Text Node as follows:
Text textRef = new Text(message);
If you consult the JavaFX API documentation, you will notice that a Text instance, contained in package
javafx.scene.text, extends a Shape that extends a Node. As a consequence, a Text instance is a Node as
well, and all the properties on Node apply on Text as well. Moreover, Text instances can be used in the scene
graph the same way other nodes are used.
As you can detect from the example, a Text instance contains a number of properties that can be
modified. Most of the properties are self-explanatory, but again, it is always useful to consult the JavaFX API
documentation when manipulating objects.
Because all graphical elements in JavaFX directly or indirectly extend the Node class, and because the
Node class already contains many useful properties, the amount of properties on a specific graphical element
such as Text can be rather high.
In our example, we set a limited number of properties that are briefly explained next.
The textRef.setLayoutY(100) method applies a vertical translation of 100 pixels to the Text content.
The fill method is used to specify the color of the text.

13

Chapter 1 ■ Getting a Jump-Start in JavaFX

While you’re looking at the javafx.scene.text package in the API documentation, take a look at the
font function of the Font class, which is used to define the font family, weight, and size of the Text.
The textOrigin property specifies how the text is aligned with its area.
Referring again to the JavaFX API documentation, notice that the VPos enum (in the javafx.geometry
package) has fields that serve as constants, for example, BASELINE, BOTTOM, and TOP. These control the
origin of the text with respect to vertical locations on the displayed Text:
•

The TOP origin, as we’re using it in the previous code snippet, places the top of the
text (including ascenders) at the layoutY position, relative to the coordinate space in
which the Text is located.

•

The BOTTOM origin would place the bottom of the text, including descenders
(located in a lowercase g, for example) at the layoutY position.

•

The BASELINE origin would place the baseline of the text (excluding descenders) at
the layoutY position. This is the default value for the textOrigin property of a Text
instance.

The wrappingWidth property enables you to specify at what number of pixels the text will wrap.
The textAlignment property enables you to control how the text will be justified. In our example,
TextAlignment.JUSTIFY aligns the text on both the left and right sides, expanding the space between words
to achieve that.
The text that we’re displaying is sufficiently long enough to wrap and be drawn on the Earth, so we need
to define a rectangular region, outside of which the text cannot be seen.

■■Tip We recommend you modify some of the values, recompile the example, and run it again. This will help
you understanding how the different properties work. Alternatively, by using ScenicView you can inspect and
modify the different properties at runtime.

Working with Graphical Nodes as a Group
One powerful graphical feature of JavaFX is the ability to create scene graphs, which consist of a tree of
graphical nodes. You can then assign values to properties of a Group located in the hierarchy, and the nodes
contained in the Group will be affected. In our current example from Listing 1-1, we’re using a Group to
contain a Text node and to clip a specific rectangular region within the Group so that the text doesn’t appear
on the moon or the Earth as it animates upward. Here’s the relevant code snippet:
Group textGroup = new Group(textRef);
textGroup.setLayoutX(50);
textGroup.setLayoutY(180);
textGroup.setClip(new Rectangle(430, 85));
Notice that the Group is located 50 pixels to the right and 180 pixels down from where it would have
been located by default. This is due to the values assigned to the layoutX and layoutY variables of the Group
instance. Because this Group is contained directly by the Scene, its upper-left corner’s location is 50 pixels
to the right and 180 pixels down from the upper-left corner of the Scene. Take a look at Figure 1-4 to see this
example illustrated as you read the rest of the explanation.

14

Chapter 1 ■ Getting a Jump-Start in JavaFX

Figure 1-4. The Scene, Group, Text, and clip illustrated
A Group instance contains instances of Node subclasses by assigning a collection of them to itself via
the children() method. In the previous code snippet, the Group contains a Text instance that has a value
assigned to its layoutY property. Because this Text is contained by a Group, it assumes the two-dimensional
space (also called the coordinate space) of the Group, with the origin of the Text node (0,0) coincident
with the top-left corner of the Group. Assigning a value of 100 to the layoutY property causes the Text to
be located 100 pixels down from the top of the Group, which is just below the bottom of the clip region,
thus causing it to be out of view until the animation begins. Because a value isn’t assigned to the layoutX
variable, its value is 0 (the default).
The layoutX and layoutY properties of the Group just described are examples of our earlier statement
that nodes contained in a Group will be affected by values assigned to properties of the Group. Another
example is setting the opacity property of a Group instance to 0.5, which causes all of the nodes contained in
that Group to become translucent. If the JavaFX API documentation is handy, look at the properties available
in the javafx.scene.Group class. Then look at the properties available in the javafx.scene.Node class
properties, which is where you’ll find the layoutX, layoutY, and opacity variables that are inherited by the
Group class.

15

Chapter 1 ■ Getting a Jump-Start in JavaFX

Clipping Graphical Areas
To define a clipping area, we assign a Node subclass to the clip property that defines the clipping shape,
in this case a Rectangle that is 430 pixels wide and 85 pixels high. In addition to keeping the Text from
covering the moon, when the Text scrolls up as a result of animation, the clipping area keeps the Text from
covering the earth.

Animating the Text to Make It Scroll Up
When the HelloEarthriseMain program is invoked, the Text begins scrolling up slowly. To achieve this
animation, we’re using the TranslateTransition class located in the javafx.animation package, as shown
in the following snippet from Listing 1-1.
TranslateTransition transTransition = new TranslateTransition(new Duration(75000), textRef);
transTransition.setToY(-820);
transTransition.setInterpolator(Interpolator.LINEAR);
transTransition.setCycleCount(Timeline.INDEFINITE);
...code omitted...
// Start the text animation
transTransition.play();
The javafx.animation package contains convenience classes for animating nodes. This
TranslateTransition instance translates the Text node referenced by the textRef variable from its original
Y position of 100 pixels to a Y position of –820 pixels, over a duration of 75 seconds. The Interpolator.
LINEAR constant is assigned to the interpolator property, which causes the animation to proceed in a linear
fashion. A look at the API docs for the Interpolator class in the javafx.animation package reveals that
there are other forms of interpolation available, one of which is EASE_OUT, which slows down the animation
toward the end of the specified duration.

■■Note Interpolation in this context is the process of calculating the value at any point in time, given a
beginning value, an ending value, and a duration.
The last line in the previous snippet begins executing the play method of the TranslateTransition
instance created earlier in the program. This makes the Text begin scrolling upward. Because of the value
assigned to the cycleCount variable, this transition will repeat indefinitely.
Now that you’ve compiled and run this example using the command-line tools and we’ve walked
through the code together, it is time to begin using the NetBeans IDE to make the development and
deployment process faster and easier.

Building and Running the Program with NetBeans
Assuming that you’ve downloaded and extracted the source code for this book into a directory, follow the
directions in this exercise to build and run the Hello Earthrise program in NetBeans. If you haven’t yet
downloaded the Java SDK and NetBeans, please do so from the site listed in the “Resources” section at the
end of this chapter.

16

Chapter 1 ■ Getting a Jump-Start in JavaFX

BUILDING AND RUNNING HELLO EARTHRISE WITH NETBEANS
To build and run the Hello Earthrise program, perform the following steps.
1.

Start NetBeans.

2.

Choose File ➤ New Project from the menu bar. The first window of the New Project
Wizard will appear. Select the JavaFX category, and you will see wizard shown in
Figure 1-5.

Figure 1-5. New Project Wizard

3.

Choose JavaFX Application in the Projects pane, and then click Next. The next page
in the New Project Wizard, shown in Figure 1-6, should appear.

17

Chapter 1 ■ Getting a Jump-Start in JavaFX

Figure 1-6. The next page of the New Project Wizard

4.

On this screen, type the project name (we used HelloEarthRise) and click Browse.

5.

Select a Project Location either by typing it directly into the text box or by
clicking Browse to navigate to the desired directory (we used /home/johan/
NetBeansProjects).

6.

Select the Create Application Class check box, and change the supplied package/
class name to projavafx.helloearthrise.ui.HelloEarthRiseMain

7.

Click Finish. The HelloEarthRise project with a default main class created by
NetBeans should now be created. If you’d like to run this default program, rightclick the HelloEarthRise project in the Projects pane and select Run Project from the
shortcut menu.

8.

Enter the code from Listing 1-1 into the HelloEarthRiseMain.java code window.
You can type it in, or cut and paste it from the HelloEarthRiseMain.java file
located in the Chapter01/HelloEarthRise/src/projavafx/helloearthrise/ui
directory of this book’s source code download.

9.

Right-click the HelloEarthRise project in the Projects pane and select Run Project
from the shortcut menu.

The HelloEarthRise program should begin executing, as you saw in Figure 1-3 earlier in the chapter.
At this point, you’ve built and run the “Hello Earthrise” program application, both from the command
line and using NetBeans. Before leaving this example, we show you another way to achieve the scrolling
Text node. There is a class in the javafx.scene.control package named ScrollPane whose purpose is

18

Chapter 1 ■ Getting a Jump-Start in JavaFX

to provide a scrollable view of a node that is typically larger than the view. In addition, the user can drag
the node being viewed within the scrollable area. Figure 1-7 shows the Hello Earthrise program after being
modified to use the ScrollPane control.

Figure 1-7. Using the ScrollPane control to provide a scrollable view of the Text node
Notice that the move cursor is visible, signifying that the user can drag the node around the clipped
area. Note that the screenshot in Figure 1-7 is of the program running on macOS X, and the move cursor has
a different appearance on other platforms. Listing 1-2 contains the relevant portion of code for this example,
named HelloScrollPaneMain.java.
Listing 1-2. The HelloScrollPaneMain.java Program
...code omitted...
    // Create a ScrollPane containing the text
        ScrollPane scrollPane = new ScrollPane();
        scrollPane.setLayoutX(50);
        scrollPane.setLayoutY(180);
        scrollPane.setPrefWidth(400);
        scrollPane.setPrefHeight(85);
        scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        scrollPane.setPannable(true);
        scrollPane.setContent(textRef);
        scrollPane.setStyle("-fx-background-color: transparent;");

19

Chapter 1 ■ Getting a Jump-Start in JavaFX

        // Combine ImageView and ScrollPane
        Group root = new Group(imageView, scrollPane);
        Scene scene = new Scene(root, 516, 387);
Now that you’ve learned some of the basics of JavaFX application development, let’s examine another
sample application to help you learn more JavaFX concepts and constructs.

Developing Your Second JavaFX Program: “More Cowbell!”
If you’re familiar with the Saturday Night Live television show, you may have seen the “More Cowbell”
sketch, in which Christopher Walken’s character keeps asking for “more cowbell” during a Blue Oyster Cult
recording session. The following JavaFX example program covers some of the simple but powerful concepts
of JavaFX in the context of an imaginary application that lets you select a music genre and control the
volume. Of course, “Cowbell Metal,” shortened to “Cowbell,” is one of the available genres. Figure 1-8 shows
a screenshot of this application, which has a sort of retro iPhone application look.

Figure 1-8. The Audio Configuration “More Cowbell” program

Building and Running the Audio Configuration Program
Earlier in the chapter, we showed you how to create a new JavaFX project in NetBeans. For this example
(and the rest of the examples in the book), we take advantage of the fact that the code download bundle for
the book contains both NetBeans and Eclipse project files for each example. Follow the instructions in this
exercise to build and run the Audio Configuration application.

20

Chapter 1 ■ Getting a Jump-Start in JavaFX

BUILDING AND RUNNING THE AUDIO CONFIGURATION
PROGRAM USING NETBEANS
To build and execute this program using NetBeans, perform the following steps.
1.

From the File menu, select the Open Project menu item. In the Open Project dialog
box, navigate to the Chapter01 directory where you extracted the book’s code
download bundle, as shown in Figure 1-9.

Figure 1-9. The Chapter 01 directory in the Open Project dialog box

2.

Select the AudioConfig project in the pane on the left, and click Open Project.

3.

Run the project as discussed previously.

The application should appear as shown in Figure 1-8.

The Behavior of the Audio Configuration Program
When you run the application, notice that adjusting the volume slider changes the associated decibel
(dB) level displayed. Also, selecting the Muting check box disables the slider, and selecting various genres
changes the volume slider. This behavior is enabled by concepts that are shown in the code that follows,
such as the following:
•

Binding to a class that contains a model

•

Using change listeners

•

Creating observable lists

21

Chapter 1 ■ Getting a Jump-Start in JavaFX

Understanding the Audio Configuration Program
The Audio Configuration program contains two source code files, shown in Listing 1-3 and Listing 1-4:
•

The AudioConfigMain.java file in Listing 1-3 contains the main class, and expresses
the UI in a manner that you are familiar with from the Hello Earthrise example in
Listing 1-1.

•

The AudioConfigModel.java file in Listing 1-4 contains a model for this program,
which holds the state of the application, to which the UI is bound.

Take a look at the AudioConfigMain.java source code in Listing 1-3, after which we examine it together,
focusing on concepts not covered in the previous example.
Listing 1-3. The AudioConfigMain.java Program
package projavafx.audioconfig.ui;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

javafx.application.Application;
javafx.geometry.VPos;
javafx.scene.Group;
javafx.scene.Scene;
javafx.scene.control.CheckBox;
javafx.scene.control.ChoiceBox;
javafx.scene.control.Slider;
javafx.scene.paint.Color;
javafx.scene.paint.CycleMethod;
javafx.scene.paint.LinearGradient;
javafx.scene.paint.Stop;
javafx.scene.shape.Line;
javafx.scene.shape.Rectangle;
javafx.scene.text.Font;
javafx.scene.text.FontWeight;
javafx.scene.text.Text;
javafx.stage.Stage;
projavafx.audioconfig.model.AudioConfigModel;

public class AudioConfigMain extends Application {
    // A reference to the model
    AudioConfigModel acModel = new AudioConfigModel();
    Text textDb;
    Slider slider;
    CheckBox mutingCheckBox;
    ChoiceBox genreChoiceBox;
    Color color = Color.color(0.66, 0.67, 0.69);
    public static void main(String[] args) {
        Application.launch(args);
    }

22

Chapter 1 ■ Getting a Jump-Start in JavaFX

    @Override
    public void start(Stage stage) {
        Text title = new Text(65,12, "Audio Configuration");
        title.setTextOrigin(VPos.TOP);
        title.setFill(Color.WHITE);
        title.setFont(Font.font("SansSerif", FontWeight.BOLD, 20));
        Text textDb = new Text();
        textDb.setLayoutX(18);
        textDb.setLayoutY(69);
        textDb.setTextOrigin(VPos.TOP);
        textDb.setFill(Color.web("#131021"));
        textDb.setFont(Font.font("SansSerif", FontWeight.BOLD, 18));
        Text mutingText = new Text(18, 113, "Muting");
        mutingText.setTextOrigin(VPos.TOP);
        mutingText.setFont(Font.font("SanSerif", FontWeight.BOLD, 18));
        mutingText.setFill(Color.web("#131021"));
        Text genreText = new Text(18,154,"Genre");
        genreText.setTextOrigin(VPos.TOP);
        genreText.setFill(Color.web("#131021"));
        genreText.setFont(Font.font("SanSerif", FontWeight.BOLD, 18));
        slider = new Slider();
        slider.setLayoutX(135);
        slider.setLayoutY(69);
        slider.setPrefWidth(162);
        slider.setMin(acModel.minDecibels);
        slider.setMax(acModel.maxDecibels);
        mutingCheckBox = new CheckBox();
        mutingCheckBox.setLayoutX(280);
        mutingCheckBox.setLayoutY(113);
        genreChoiceBox = new ChoiceBox();
        genreChoiceBox.setLayoutX(204);
        genreChoiceBox.setLayoutY(154);
        genreChoiceBox.setPrefWidth(93);
        genreChoiceBox.setItems(acModel.genres);
        Stop[] stops = new Stop[]{new Stop(0, Color.web("0xAEBBCC")), new Stop(1, Color.
web("0x6D84A3"))};
        LinearGradient linearGradient = new LinearGradient(0, 0, 0, 1, true, CycleMethod.
NO_CYCLE, stops);
        Rectangle rectangle = new Rectangle(0, 0, 320, 45);
        rectangle.setFill(linearGradient);
        Rectangle rectangle2 = new Rectangle(0, 43, 320, 300);
        rectangle2.setFill(Color.rgb(199, 206, 213));

23

Chapter 1 ■ Getting a Jump-Start in JavaFX

        Rectangle rectangle3 = new Rectangle(8, 54, 300, 130);
        rectangle3.setArcHeight(20);
        rectangle3.setArcWidth(20);
        rectangle3.setFill(Color.WHITE);
        rectangle3.setStroke(color);
        Line line1 = new Line(9, 97, 309, 97);
        line1.setStroke(color);
        Line line2 = new Line(9, 141, 309, 141);
        line2.setFill(color);
        Group group = new Group(rectangle, title, rectangle2, rectangle3,
                textDb,
                slider,
                line1,
                mutingText,
                mutingCheckBox, line2, genreText,
                genreChoiceBox);
        Scene scene = new Scene(group, 320, 343);
        textDb.textProperty().bind(acModel.selectedDBs.asString().concat(" dB"));
        slider.valueProperty().bindBidirectional(acModel.selectedDBs);
        slider.disableProperty().bind(acModel.muting);
        mutingCheckBox.selectedProperty().bindBidirectional(acModel.muting);
        acModel.genreSelectionModel = genreChoiceBox.getSelectionModel();
        acModel.addListenerToGenreSelectionModel();
        acModel.genreSelectionModel.selectFirst();
        stage.setScene(scene);
        stage.setTitle("Audio Configuration");
        stage.show();
    }
}
Now that you’ve seen the main class in this application, let’s walk through the new concepts.

The Magic of Binding
One of the most powerful aspects of JavaFX is binding, which enables the application’s UI to easily stay in
sync with the state, or model, of the application. The model for a JavaFX application is typically held in one
or more classes, in this case the AudioConfigModel class. Look at the following snippet, taken from
Listing 1-3, in which we create an instance of this model class.
  AudioConfigModel acModel = new AudioConfigModel();
There are several graphical node instances in the scene of this UI (recall that a scene consists of a
sequence of nodes). Skipping past several of them, we come to the graphical nodes shown in the following
snippet that have a property bound to the selectedDBs property in the model.

24

Chapter 1 ■ Getting a Jump-Start in JavaFX

textDb = new Text();
... code omitted
slider = new Slider();
...code omitted...
textDb.textProperty().bind(acModel.selectedDBs.asString().concat(" dB"));
slider.valueProperty().bindBidirectional(acModel.selectedDBs);
As shown in this code, the text property of the Text object is bound to an expression. The bind function
contains an expression (that includes the selectedDBs property), which is evaluated and becomes the value
of the text property. Look at Figure 1-9 (or check the running application) to see the content value of the Text
node displayed to the left of the slider.
Notice also in the code that the value property of the Slider node is bound to the selectedDBs
property in the model as well, but that it uses the bindBidirectional() method. This causes the bind to
be bidirectional, so in this case when the slider is moved, the selectedDBs property in the model changes.
Conversely, when the selectedDBs property changes (as a result of changing the genre), the slider moves.
Go ahead and move the slider to demonstrate the effects of the bind expressions in the snippet. The
number of decibels displayed at the left of the slider should change as the slider is adjusted.
There are other bound properties in Listing 1-3 that we point out when we walk through the model
class. Before leaving the UI, we point out some color-related concepts in this example.

Colors and Gradients
The following snippet from Listing 1-3 contains an example of defining a color gradient pattern, as well as
defining colors.
Stop[] stops = new Stop[]{new Stop(0, Color.web("0xAEBBCC")), new Stop(1, Color.
web("0x6D84A3"))};
LinearGradient linearGradient = new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE,
stops);
Rectangle rectangle = new Rectangle(0, 0, 320, 45);
rectangle.setFill(linearGradient);
If the JavaFX API docs are handy, first take a look at the javafx.scene.shape.Rectangle class and
notice that it inherits a property named fill that is of type javafx.scene.paint.Paint. Looking at
the JavaFX API docs for the Paint class, you’ll see that the Color, ImagePattern, LinearGradient, and
RadialGradient classes are subclasses of Paint. This means that the fill of any shape can be assigned a
color, pattern, or gradient.
To create a LinearGradient, as shown in the code, you need to define at least two stops, which define
the location and color at that location. In this example, the offset value of the first stop is 0.0, and the offset
value of the second stop is 1.0. These are the values at both extremes of the unit square, the result being that
the gradient will span the entire node (in this case a Rectangle). The direction of the LinearGradient is
controlled by its startX, startY, endX, and endY values, which we pass via the constructor. In this case, the
direction is only vertical because the startY value is 0.0 and the endY value is 1.0, whereas the startX and
endX values are both 0.0.
Note that in the Hello Earthrise example in Listing 1-1, the constant named Color.WHITE was used to
represent the color white. In the previous snippet, the web function of the Color class is used to define a
color from a hexadecimal value.

25

Chapter 1 ■ Getting a Jump-Start in JavaFX

The Model Class for the Audio Configuration Example
Take a look at the source code for the AudioConfigModel class in Listing 1-4.
Listing 1-4. The Source Code for AudioConfigModel.java
package projavafx.audioconfig.model;
import
import
import
import
import
import
import
import

javafx.beans.Observable;
javafx.beans.property.BooleanProperty;
javafx.beans.property.IntegerProperty;
javafx.beans.property.SimpleBooleanProperty;
javafx.beans.property.SimpleIntegerProperty;
javafx.collections.FXCollections;
javafx.collections.ObservableList;
javafx.scene.control.SingleSelectionModel;

/**
* The model class that the AudioConfigMain class uses
*/
public class AudioConfigModel {
  /**
   * The minimum audio volume in decibels
   */
  public double minDecibels = 0.0;
  /**
   * The maximum audio volume in decibels
   */
  public double maxDecibels = 160.0;
  /**
   * The selected audio volume in decibels
   */
  public IntegerProperty selectedDBs = new SimpleIntegerProperty(0);
  /**
   * Indicates whether audio is muted
   */
  public BooleanProperty muting = new SimpleBooleanProperty(false);
  /**
   * List of some musical genres
   */
  public ObservableList genres = FXCollections.observableArrayList(
    "Chamber",
    "Country",
    "Cowbell",
    "Metal",
    "Polka",
    "Rock"
  );

26

Chapter 1 ■ Getting a Jump-Start in JavaFX

  /**
   * A reference to the selection model used by the Slider
   */
  public SingleSelectionModel genreSelectionModel;
  /**
   * Adds a change listener to the selection model of the ChoiceBox, and contains
   * code that executes when the selection in the ChoiceBox changes.
   */
public void addListenerToGenreSelectionModel() {
    genreSelectionModel.selectedIndexProperty().addListener((Observable o) -> {
        int selectedIndex = genreSelectionModel.selectedIndexProperty().getValue();
        switch(selectedIndex) {
            case 0: selectedDBs.setValue(80);
            break;
            case 1: selectedDBs.setValue(100);
            break;
            case 2: selectedDBs.setValue(150);
            break;
            case 3: selectedDBs.setValue(140);
            break;
            case 4: selectedDBs.setValue(120);
            break;
            case 5: selectedDBs.setValue(130);
        }
    });
  }
}

Using InvalidationListeners and Lambda Expressions
In the “The Magic of Binding” section, we showed how you can use property binding for dynamically
changing parameters. There is another, more low-level but also more flexible way of achieving this, using
ChangeListeners and InvalidationListeners. These concepts are discussed in more detail in Chapter 4.
In our example, we add an InvalidationListener to the selectedIndexProperty of the
genreSelectionModel. When the value of the selectedIndexProperty changes, and when we didn’t retrieve
it yet, the invalidated(Observable) method on the added InvalidationListener will be called. In the
implementation of this method, we retrieve the value of the selectedIndexProperty, and based on its value,
the value of the selectedDBs property is changed. This is achieved with the following code:
public void addListenerToGenreSelectionModel() {
    genreSelectionModel.selectedIndexProperty().addListener((Observable o) -> {
        int selectedIndex = genreSelectionModel.selectedIndexProperty().getValue();
        switch(selectedIndex) {
            case 0: selectedDBs.setValue(80);
            break;
            case 1: selectedDBs.setValue(100);
            break;
            case 2: selectedDBs.setValue(150);
            break;

27

Chapter 1 ■ Getting a Jump-Start in JavaFX

            case 3: selectedDBs.setValue(140);
            break;
            case 4: selectedDBs.setValue(120);
            break;
            case 5: selectedDBs.setValue(130);
        }
    });
  }
Note that we are using a lambda expression here rather than creating a new instance of the
InvalidationListener and implementing its single abstract method invalidated.

■■Tip One of the major enhancements in JavaFX 8 is the fact that it is using Java 8. As a consequence,
abstract classes with a single abstract method can easily be replaced by lambda expressions, which clearly
enhance the readability of the code.
What causes selectedIndexProperty of the genreSelectionModel to change? To see the answer to this,
we have to revisit some code in Listing 1-3. In the following code snippet, the setItems method of ChoiceBox
is used to populate the ChoiceBox with items that each contain a genre.
genreChoiceBox = new ChoiceBox();
genreChoiceBox.setLayoutX(204);
genreChoiceBox.setLayoutY(154);
genreChoiceBox.setPrefWidth(93);
genreChoiceBox.setItems(acModel.genres);
This snippet from the model code in Listing 1-4 contains the collection to which the ComboBox items are
bound:
/**
* List of some musical genres
*/
public ObservableList genres = FXCollections.observableArrayList(
  "Chamber",
  "Country",
  "Cowbell",
  "Metal",
  "Polka",
  "Rock"
);
When the user chooses a different item in the ChoiceBox, the invalidationListener is invoked.
Looking again at the code in the invalidationListener, you’ll see that the value of the selectedDBs
property changes, which as you may recall, is bidirectionally bound to the slider. This is why the slider moves
when you select a genre in the combo box. Go ahead and test this by running the Audio Config program.

28

Chapter 1 ■ Getting a Jump-Start in JavaFX

■■Note

Associating the items property of the ChoiceBox with an ObservableList causes the items in the

ChoiceBox to be automatically updated when the elements in the underlying collection are modified.

Surveying JavaFX Features
We close this chapter by surveying many of the features of JavaFX, some of which are a review for you. We do
this by describing several of the more commonly used packages and classes in the Java SDK API.
The javafx.stage package contains the following:
•

The Stage class, which is the top level of the UI containment hierarchy for any
JavaFX application, regardless of where it is deployed (e.g., the desktop, a browser, or
a cell phone).

•

The Screen class, which represents the displays on the machine in which a JavaFX
program is running. This enables you to get information about the screens, such as
size and resolution.

The javafx.scene package contains some classes that you’ll use often:
•

The Scene class is the second level of the UI containment hierarchy for JavaFX
applications. It includes all of the UI elements contained in the application. These
elements are called graphical nodes, or simply nodes.

•

The Node class is the base class of all of the graphical nodes in JavaFX. UI elements
such as text, images, media, shapes, and controls (e.g., text boxes and buttons) are all
subclasses of Node. Take a moment to look at the variables and functions in the Node
class to appreciate the capabilities provided to all of its subclasses, including bounds
calculation and mouse and keyboard event handling.

•

The Group class is a subclass of the Node class. Its purpose includes grouping nodes
together into a single coordinate space and allowing transforms (e.g., rotate) to be
applied to the whole group. Also, attributes of the group that are changed
(e.g., opacity) apply to all of the nodes contained within the group.

Several packages begin with javafx.scene that contain subclasses of Node of various types. Examples
include the following:
•

The javafx.scene.image package contains the Image and ImageView classes,
which enable images to be displayed in the Scene. The ImageView class is a
subclass of Node.

•

The javafx.scene.shape package contains several classes for drawing shapes such
as Circle, Rectangle, Line, Polygon, and Arc. The base class of the shapes, named
Shape, contains an attribute named fill that enables you to specify a color, pattern,
or gradient with which to fill the shape.

•

The javafx.scene.text package contains the Text class for drawing text in the
scene. The Font class enables you to specify the font name and size of the text.

•

The javafx.scene.media package has classes that enable you to play media. The
MediaView class is a subclass of Node that displays the media.

•

The javafx.scene.chart package has classes that help you easily create area, bar,
bubble, line, pie, and scatter charts. The corresponding UI classes in this package are
AreaChart, BarChart, BubbleChart, LineChart, PieChart, and ScatterChart.

29

Chapter 1 ■ Getting a Jump-Start in JavaFX

Here are some other packages in the JavaFX 8 API.
•

The javafx.scene.control package contains several UI controls, each one having
the ability to be skinned and styled via CSS.

•

The javafx.scene.transform package enables you to transform nodes (scale, rotate,
translate, shear, and affine).

•

The javafx.scene.input package contains classes such as MouseEvent and
KeyEvent that provide information about these events from within an event handler
function such as the Node class’s onMouseClicked event.

•

The javafx.scene.layout package contains several layout containers, including
HBox, VBox, BorderPane, FlowPane, StackPane, and TilePane.

•

The javafx.scene.effect package contains easy-to-use effects such as Reflection,
Glow, Shadow, BoxBlur, and Lighting.

•

The javafx.scene.web package contains classes for easily embedding a web
browser in your JavaFX applications.

•

The javafx.animation package contains time-based interpolations typically used
for animation and convenience classes for common transitions.

•

The javafx.beans, javafx.beans.binding, javafx.beans.property, and javafx.
beans.value packages contain classes that implement properties and binding.

•

The javafx.fxml package contains classes that implement a very powerful facility
known as FXML, a markup language for expressing JavaFX UIs in XML.

•

The javafx.util package contains utility classes such as the Duration class used in
the HelloEarthRise example.

•

The javafx.print package contains utilities for printing (parts of ) the layout of a
JavaFX application.

•

The javafx.embed.swing package contains the required functionality for embedded
JavaFX applications in a Swing application.

•

The javafx.embed.swt package contains the required functionality for embedding
JavaFX applications in an SWT application.

Take a look at the JavaFX API docs again in light of this information to get a deeper sense of how you can
use its capabilities.

Summary
Congratulations! You learned a lot about JavaFX in this chapter, including

30

•

JavaFX is rich-client Java, and is needed by the software development industry.

•

Since the Java 9 release, the JavaFX APIs are split in a number of modules that follow
the Java 9 conventions and rules.

•

Some of the high points of the history of JavaFX.

•

Where to find JavaFX resources, including the Java SDK, NetBeans, Scene Builder,
ScenicView, and the API documentation.

Chapter 1 ■ Getting a Jump-Start in JavaFX

•

How to compile and run a JavaFX program from the command line.

•

How to build and run a JavaFX program using NetBeans.

•

How to use several of the classes in the JavaFX API.

•

How to create a class in JavaFX and use it as a model that contains the state of a
JavaFX application.

•

How to use property binding to keep the UI easily in sync with the model.

We also looked at many of the available API packages and classes, and you learned how you can
leverage their capabilities. Now that you have a jump-start in JavaFX, you can begin examining the details of
JavaFX in Chapter 2.

Resources
For some background information on JavaFX, you can consult the following resources.
•

This book’s code examples: The Source Code/Download section on the Apress
web site (www.apress.com).

•

Java Posse #163: Newscast for February 8, 2008: This is a podcast of a Java Posse
interview with Josh Marinacci and Richard Bair on the subject of JavaFX.
(http://javaposse.com/java_posse_163_newscast_for_feb_8th_2008).

•

“Congratulations to the JavaFX Script Compiler Team—The Elephant Is Through the
Door”: A blog post by one of this book’s authors, Jim Weaver, that congratulated the
JavaFX compiler team for reaching a tipping point in the project.
(http://learnjavafx.typepad.com/weblog/2007/12/congratulations.html).

•

Oracle’s JavaFX.com site: The home page for JavaFX where you can download the
JavaFX SDK and other resource for JavaFX. (www.javafx.com)

•

FX Experience: A blog maintained by Oracle JavaFX Engineers Richard Bair, Jasper
Potts, and Jonathan Giles. (http://fxexperience.com)

•

Jim Weaver’s JavaFX Blog: A blog, started in October 2007, the stated purpose of
which is to help the reader become a “JavaFXpert.” (http://javafxpert.com)

•

Weiqi Gao’s Observation: A blog in which Weiqi Gao shares his experience in
software development. (http://weiqigao.blogspot.com)

•

Dean Iverson’s Pleasing Software Blog: A blog in which Dean Iverson shares his
innovations in JavaFX and GroovyFX. (http://pleasingsoftware.blogspot.com)

•

Steve on Java: A blog in which Stephen Chin keeps the world updated on his tireless
exploits in the areas of JavaFX, Java, and Agile development. (http://steveonjava.com)

•

Johan’s blog: A blog in which Johan Vos discusses JavaFX and Java Enterprise.
(http://blogs.lodgon.com/johan)

•

JavaFX Eclipse Plugin: Eclipse tooling for JavaFX 2.0, developed by Tom Shindl.
(http://tomsondev.bestsolution.at/2011/06/24/introducing-efxclipse/)

31

Chapter 1 ■ Getting a Jump-Start in JavaFX

32

•

Scenic View: An application for inspecting the scenegraph of your JavaFX
applications. (http://scenic-view.org)

•

Gluon web site: (http://gluonhq.com)

•

ControlsFX, high-quality custom JavaFX controls. (http://controlsfx.org)

•

JFXtras.org, high-quality custom JavaFX controls. (http://jfxtras.org)

CHAPTER 2

Creating a User Interface in JavaFX
Life is the art of drawing without an eraser.
—John W. Gardner
Chapter 1 gave you a jump start using JavaFX by covering the basics in developing and executing JavaFX
programs. Now we cover many of the details about creating a UI in JavaFX that were glossed over in Chapter 1.
First on the agenda is to get you acquainted with the theater metaphor used by JavaFX to express UIs and to
cover the significance of what we call a node-centric UI.

Programmatic vs. Declarative Creation of the User Interface
The JavaFX platform provides two complementary ways for creating a UI. In this chapter, we discuss how
you can use the Java API to create and populate a UI. This is a convenient way for Java developers who are
used to writing code to leverage APIs.
Designers often use graphical tools that allow them to declare rather than program a UI. The JavaFX
platform defines FXML, which is an XML-based markup language that can be used to declaratively describe
a UI. Furthermore, a graphical tool called Scene Builder is made available by Gluon, and this tool is capable
of working with FXML files. The use of Scene Builder is demonstrated in Chapter 4.
Note that parts of a UI can be created using the API, where other parts can be created using Scene
Builder. The FXML APIs provide the bridge and the integration glue between the two approaches.

Introduction to Node-Centric UIs
Creating a UI in JavaFX is like creating a theater play, in that it typically consists of these very simple steps:
1.

Create a stage on which your program will perform. The realization of your stage
will depend on the platform on which it is deployed (e.g., a desktop, a tablet, or
an embedded system).

2.

Create a scene in which the actors and props (nodes) will visually interact with
each other and the audience (the users of your program). Like any good set
designer in the theater business, good JavaFX developers endeavor to make their
scenes visually appealing. To this end, it is often a good idea to collaborate with a
graphic designer on your “theater play.”

© Johan Vos, Stephen Chin, Weiqi Gao, James Weaver, and Dean Iverson 2018
J. Vos et al., Pro JavaFX 9, https://doi.org/10.1007/978-1-4842-3042-8_2

33

Chapter 2 ■ Creating a User Interface in JavaFX

3.

Create nodes in the scene. These nodes are subclasses of the javafx.scene.Node
class, which include UI controls, shapes, Text (a type of shape), images, media
players, embedded browsers, and custom UI components that you create. Nodes
can also be containers for other nodes, often providing cross-platform layout
capabilities. A scene has a scene graph that contains a directed graph of nodes.
Individual nodes and groups of nodes can be manipulated in many ways
(e.g., moving, scaling, and setting opacity) by changing the values of a very rich
set of Node properties.

4.

Create variables and classes that represent the model for the nodes in the scene.
As discussed in Chapter 1, one of the very powerful aspects of JavaFX is binding,
which enables the application’s UI to stay in sync easily with the state, or model,
of the application.

■■Note Most of the examples in this chapter are small programs intended to demonstrate UI concepts. For
this reason, the model in many of these examples consists of variables appearing in the main program, rather
than being contained by separate Java classes (e.g., the AudioConfigModel class in Chapter 1).
5.

Create event handlers, such as onMousePressed, that allow the user to interact
with your program. Often these event handlers manipulate instance variables
in the model. Many of these handlers require a single abstract method to be
implemented, and as a consequence provide a perfect opportunity to use
lambda expressions.

6.

Create timelines and transitions that animate your scene. For example, you might
want the thumbnail images of a list of books to move smoothly across the scene
or a page in the UI to fade into view. You might simply want a ping-pong ball to
move across the scene, bouncing off walls and paddles; this is demonstrated later
in this chapter in the “The Zen of Node Collision Detection” section.

Let’s get started with a closer look at step 1, in which we examine the capabilities of the stage.

Setting the Stage
The appearance and functionality of your stage will depend on the platform on which it is deployed. For
example, if deployed in a mobile device, or an embedded device with a touch screen, your stage might be the
whole touch screen. The stage for a JavaFX program deployed in an X11 system will be a window.

Understanding the Stage Class
The Stage class is the top-level container for any JavaFX program that has a graphical UI. It has several
properties and methods that allow it, for example, to be positioned, sized, given a title, made invisible, or
given some degree of opacity. The two best ways that we know of to learn the capabilities of a class are to
study the JavaFX API documentation and to examine (and write) programs that use it. In this section, we ask
you to do both, beginning with looking at the API docs.

34

Chapter 2 ■ Creating a User Interface in JavaFX

The JavaFX API docs, just like the other Java API docs, are available online at the http://download.java.
net/java/jdk9/docs/api/overview-summary. Open the index.html file in your browser, navigate to the
javafx.stage package in the javafx.graphics module, and select the Stage class. That page should contain
tables of Properties, Constructors, and Methods, including select ones shown in the excerpt in Figure 2-1.

Figure 2-1. A portion of the Stage class documentation in the JavaFX API
Go ahead and explore the documentation for each of the properties and methods in the Stage class,
remembering to click the links to reveal more detailed information. When you’re finished, come back
and we’ll show you a program that demonstrates many of the properties and methods available in the
Stage class.

Using the Stage Class: The StageCoach Example
A screenshot of the unassuming, purposely ill-fitting StageCoach example program is shown in Figure 2-2.

35

Chapter 2 ■ Creating a User Interface in JavaFX

Figure 2-2. A screenshot of the StageCoach example
The StageCoach program was created to coach you through the finer points of using the Stage class
and related classes such as StageStyle and Screen. Also, we use this program to show you how to get
arguments passed into the program. Before walking through the behavior of the program, go ahead and
open the project. Follow the instructions for building and executing the Audio-Config project in Chapter 1.
The project file is located in the Chapter02 directory subordinate to where you extracted the book’s code
download bundle.

EXAMINING THE BEHAVIOR OF THE STAGECOACH PROGRAM
When the program starts, its appearance should be similar to the screenshot in Figure 2-2. To fully
examine its behavior, perform the following steps. Note that for instructional purposes, the property and
method names on the UI correspond to the properties and methods in the Stage instance.
Notice that the StageCoach program’s window is initially displayed near the top of the screen, with its
horizontal position in the center of the screen. Drag the program’s window and observe that the x and y
values near the top of the UI are dynamically updated to reflect its position on the screen.
Resize the program’s window and observe that the width and height values change to reflect the
width and height of the Stage. Note that this size includes the decorations (title bar and borders) of the
window.
Click the program (or cause it to be in focus some other way) and notice that the focused value is true.
Cause the window to lose focus, perhaps by clicking somewhere else on the screen, and notice that the
focused value becomes false.

36

Chapter 2 ■ Creating a User Interface in JavaFX

Clear the resizable check box and then notice that the resizable value becomes false. Then try to resize
the window and note that it is not permitted. Select the resizable check box again to make the window
resizable.
Select the fullScreen check box. Notice that the program occupies the full screen and that the window
decorations are not visible. Clear the fullScreen check box to restore the program to its former size.
Edit the text in the text field beside the title label, noticing that the text in the window’s title bar is
changed to reflect the new value.
Drag the window to partially cover another window, and click toBack(). Notice that this places the
program behind the other window, therefore causing the z-order to change.
With a portion of the program’s window behind another window, but with the toFront() button visible,
click that button. Notice that the program’s window is placed in front of the other window.
Click close(), noticing that the program exits.
Invoke the program again, passing in the string "undecorated". If invoking from NetBeans, use the
Project Properties dialog box to pass this argument as shown in Figure 2-3. The "undecorated" string
is passed as a parameter without a value.

Figure 2-3. Using NetBeans’ Project Properties dialog box to pass an argument into the program

37

Chapter 2 ■ Creating a User Interface in JavaFX

Notice that this time the program appears without any window decorations, but the white background
of the program includes the background of the window. The black outline in the screenshot shown in
Figure 2-4 is part of the desktop background.
Exit the program again by clicking close( ), and then run the program again, passing in the string
"transparent" as the argument. Notice that the program appears in the shape of a rounded rectangle,
as shown in Figure 2-5.

Figure 2-4. The StageCoach program after being invoked with the undecorated argument

■■Note You might have noticed that the screenshots in Figures 2-4 and 2-5 have y values that are negative.
This is because the application was positioned on the secondary monitor, logically above the primary monitor,
when the screenshots were taken.

38

Chapter 2 ■ Creating a User Interface in JavaFX

Figure 2-5. The StageCoach program after being invoked with the transparent argument

Click the application’s UI, drag it around the screen, and click close( )when finished. Congratulations
on sticking with this 13-step exercise! Performing this exercise has prepared you to relate to the code
behind it, which we now walk through together.

Understanding the StageCoach Program
Take a look at the code for the StageCoach program in Listing 2-1 before we point out new and relevant
concepts.
Listing 2-1. StageCoachMain.java
package projavafx.stagecoach.ui;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Rectangle2D;
import javafx.geometry.VPos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

39

Chapter 2 ■ Creating a User Interface in JavaFX

import
import
import
import
import
import
import

javafx.scene.paint.Color;
javafx.scene.shape.Rectangle;
javafx.scene.text.Text;
javafx.stage.Screen;
javafx.stage.Stage;
javafx.stage.StageStyle;
javafx.stage.WindowEvent;

public class StageCoachMain extends Application {
    StringProperty title = new SimpleStringProperty();
    Text textStageX;
    Text textStageY;
    Text textStageW;
    Text textStageH;
    Text textStageF;
    CheckBox checkBoxResizable;
    CheckBox checkBoxFullScreen;
    double dragAnchorX;
    double dragAnchorY;
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        StageStyle stageStyle = StageStyle.DECORATED;
        List unnamedParams = getParameters().getUnnamed();
        if (unnamedParams.size() > 0) {
            String stageStyleParam = unnamedParams.get(0);
            if (stageStyleParam.equalsIgnoreCase("transparent")) {
                stageStyle = StageStyle.TRANSPARENT;
            } else if (stageStyleParam.equalsIgnoreCase("undecorated")) {
                stageStyle = StageStyle.UNDECORATED;
            } else if (stageStyleParam.equalsIgnoreCase("utility")) {
                stageStyle = StageStyle.UTILITY;
            }
        }
        final Stage stageRef = stage;
        Group rootGroup;
        TextField titleTextField;
        Button toBackButton = new Button("toBack()");
        toBackButton.setOnAction(e -> stageRef.toBack());
        Button toFrontButton = new Button("toFront()");
        toFrontButton.setOnAction(e -> stageRef.toFront());
        Button closeButton = new Button("close()");
        closeButton.setOnAction(e -> stageRef.close());
        Rectangle blue = new Rectangle(250, 350, Color.SKYBLUE);

40

Chapter 2 ■ Creating a User Interface in JavaFX

        blue.setArcHeight(50);
        blue.setArcWidth(50);
        textStageX = new Text();
        textStageX.setTextOrigin(VPos.TOP);
        textStageY = new Text();
        textStageY.setTextOrigin(VPos.TOP);
        textStageH = new Text();
        textStageH.setTextOrigin(VPos.TOP);
        textStageW = new Text();
        textStageW.setTextOrigin(VPos.TOP);
        textStageF = new Text();
        textStageF.setTextOrigin(VPos.TOP);
        checkBoxResizable = new CheckBox("resizable");
        checkBoxResizable.setDisable(stageStyle == StageStyle.TRANSPARENT
                || stageStyle == StageStyle.UNDECORATED);
        checkBoxFullScreen = new CheckBox("fullScreen");
        titleTextField = new TextField("Stage Coach");
        Label titleLabel = new Label("title");
        HBox titleBox = new HBox(titleLabel, titleTextField);
        VBox contentBox = new VBox(
                textStageX, textStageY, textStageW, textStageH, textStageF,
                checkBoxResizable, checkBoxFullScreen,
                titleBox, toBackButton, toFrontButton, closeButton);
        contentBox.setLayoutX(30);
        contentBox.setLayoutY(20);
        contentBox.setSpacing(10);
        rootGroup = new Group(blue, contentBox);
        Scene scene = new Scene(rootGroup, 270, 370);
        scene.setFill(Color.TRANSPARENT);
        //when mouse button is pressed, save the initial position of screen
        rootGroup.setOnMousePressed((MouseEvent me) -> {
            dragAnchorX = me.getScreenX() - stageRef.getX();
            dragAnchorY = me.getScreenY() - stageRef.getY();
        });
        //when screen is dragged, translate it accordingly
        rootGroup.setOnMouseDragged((MouseEvent me) -> {
            stageRef.setX(me.getScreenX() - dragAnchorX);
            stageRef.setY(me.getScreenY() - dragAnchorY);
        });
        textStageX.textProperty().bind(new SimpleStringProperty("x: ")
                .concat(stageRef.xProperty().asString()));
        textStageY.textProperty().bind(new SimpleStringProperty("y: ")
                .concat(stageRef.yProperty().asString()));
        textStageW.textProperty().bind(new SimpleStringProperty("width: ")
                .concat(stageRef.widthProperty().asString()));
        textStageH.textProperty().bind(new SimpleStringProperty("height: ")
                .concat(stageRef.heightProperty().asString()));

41

Chapter 2 ■ Creating a User Interface in JavaFX

        textStageF.textProperty().bind(new SimpleStringProperty("focused: ")
                .concat(stageRef.focusedProperty().asString()));
        stage.setResizable(true);
        checkBoxResizable.selectedProperty()
                .bindBidirectional(stage.resizableProperty());
        checkBoxFullScreen.selectedProperty().addListener((ov, oldValue, newValue) -> {
            stageRef.setFullScreen(checkBoxFullScreen.selectedProperty().getValue());
        });
        title.bind(titleTextField.textProperty());
        stage.setScene(scene);
        stage.titleProperty().bind(title);
        stage.initStyle(stageStyle);
        stage.setOnCloseRequest((WindowEvent we) -> {
            System.out.println("Stage is closing");
        });
        stage.show();
        Rectangle2D primScreenBounds = Screen.getPrimary().getVisualBounds();
        stage.setX((primScreenBounds.getWidth() - stage.getWidth()) / 2);
        stage.setY((primScreenBounds.getHeight() - stage.getHeight()) / 4);
    }
}

Obtaining Program Arguments
The first new concept introduced by this program is the ability to read the arguments passed into a JavaFX
program. The javafx.application package includes a class named Application that has application life
cycle-related methods such as launch(), init(), start(), and stop(). Another method in the Application
class is getParameters(), which gives the application access to the arguments passed on the command line,
as well as unnamed parameters and  pairs specified in a JNLP file. Here’s the relevant code
snippet from Listing 2-1 for your convenience:
StageStyle stageStyle = StageStyle.DECORATED;
List unnamedParams = getParameters().getUnnamed();
if (unnamedParams.size() > 0) {
  String stageStyleParam = unnamedParams.get(0);
  if (stageStyleParam.equalsIgnoreCase("transparent")) {
    stageStyle = StageStyle.TRANSPARENT;
  }
  else if (stageStyleParam.equalsIgnoreCase("undecorated")) {
    stageStyle = StageStyle.UNDECORATED;
  }
  else if (stageStyleParam.equalsIgnoreCase("utility")) {
    stageStyle = StageStyle.UTILITY;
  }
}
...code omitted...
stage.initStyle(stageStyle);

42

Chapter 2 ■ Creating a User Interface in JavaFX

Setting the Style of the Stage
We’re using the getParameters() method described previously to get an argument that tells us whether the
stage style of the Stage instance should be its default (StageStyle.DECORATED), StageStyle.UNDECORATED,
or StageStyle.TRANSPARENT. You saw the effects of each in the preceding exercise, specifically in Figures 2-2,
2-4, and 2-5.

Controlling Whether a Stage Is Resizable
As shown in the following excerpt from Listing 2-1, to make this application’s window initially resizable
we’re calling the setResizable() method of the Stage instance. To keep the resizable property of the Stage
and the state of the resizable check box synchronized, the check box is bidirectionally bound to the resizable
property of the Stage instance.
stage.setResizable(true);
checkBoxResizable.selectedProperty()
        .bindBidirectional(stage.resizableProperty());

■■Tip A property that is bound cannot be explicitly set. In the code preceding the snippet, the resizable
property is set with the setResizable() method before the property is bound in the next line.

Making a Stage Full Screen
Making the Stage show in full-screen mode is done by setting the fullScreen property of the Stage instance
to true. As shown in the following snippet from Listing 2-1, to keep the fullScreen property of the Stage and
the state of the fullScreen check box synchronized, the fullScreen property of the Stage instance is updated
whenever the selected property of the checkBox changes.
checkBoxFullScreen.selectedProperty().addListener((ov, oldValue, newValue) -> {
    stageRef.setFullScreen(checkBoxFullScreen.selectedProperty().getValue());
});
Note that the full-screen mode doesn’t have an impact on some platforms. On mobile, for example, the
JavaFX applications will by default be in full-screen mode, and the distribution for JavaFX on Mobile does
not allow a non-full-screen option because that does not make sense in the world of mobile apps on devices.

Working with the Bounds of the Stage
The bounds of the Stage are represented by its x, y, width, and height properties, the values of which can
be changed at will. This is demonstrated in the following snippet from Listing 2-1 where the Stage is placed
near the top and centered horizontally on the primary screen after the Stage has been initialized.
Rectangle2D primScreenBounds = Screen.getPrimary().getVisualBounds();
stage.setX((primScreenBounds.getWidth() - stage.getWidth()) / 2);
stage.setY((primScreenBounds.getHeight() - stage.getHeight()) / 4);

43

Chapter 2 ■ Creating a User Interface in JavaFX

We’re using the Screen class of the javafx.stage package to get the dimensions of the primary screen
so that the desired position may be calculated.

■■Note We intentionally made the Stage in Figure 2-2 larger than the Scene contained within to make the
following point. The width and height of a Stage include its decorations (title bar and border), which vary on
different platforms. It is therefore usually better to control the width and height of the Scene (we show you how
in a bit) and let the Stage conform to that size.

Drawing Rounded Rectangles
As pointed out in Chapter 1, you can put rounded corners on a Rectangle by specifying the arcWidth and
arcHeight for the corners. The following snippet from Listing 2-1 draws the sky-blue rounded rectangle that
becomes the background for the transparent window example in Figure 2-5.
Rectangle blue = new Rectangle(250, 350, Color.SKYBLUE);
blue.setArcHeight(50);
blue.setArcWidth(50);
In this snippet, we use the three-argument constructor of Rectangle, in which the first two parameters
specify the width and the height of the Rectangle. The third parameter defines the fill color of the
Rectangle.
As you can detect from this code snippet, rounded rectangles are easily created using the
arcWidth(double v) and arcHeight(double v) methods, where the parameter v defines the diameter of
the arc.

Dragging the Stage on the Desktop When a Title Bar Isn’t Available
The Stage may be dragged on the desktop using its title bar, but in the case where its StageStyle is
UNDECORATED or TRANSPARENT, the title bar isn’t available. To allow dragging in this circumstance, we added
the code shown in the following code snippet from Listing 2-1.
//when mouse button is pressed, save the initial position of screen
rootGroup.setOnMousePressed((MouseEvent me) -> {
    dragAnchorX = me.getScreenX() - stageRef.getX();
    dragAnchorY = me.getScreenY() - stageRef.getY();
});
//when screen is dragged, translate it accordingly
rootGroup.setOnMouseDragged((MouseEvent me) -> {
    stageRef.setX(me.getScreenX() - dragAnchorX);
    stageRef.setY(me.getScreenY() - dragAnchorY);
});
Event handlers are covered a little later in the chapter, but as a preview, the lambda expression that is
supplied to the onMouseDragged() method is called when the mouse is dragged. As a result, the values of the
x and y properties are altered by the number of pixels that the mouse was dragged, which moves the Stage
as the mouse is dragged.

44

Chapter 2 ■ Creating a User Interface in JavaFX

Using UI Layout Containers
When developing applications that will be deployed in a cross-platform environment or are
internationalized, it is good to use layout containers. One advantage of using layout containers is that when
the node sizes change, their visual relationships with each other are predictable. Another advantage is that
you don’t have to calculate the location of each node that you place in the UI.
The following snippet from Listing 2-1 shows how the VBox layout class, located in the javafx.
scene.layout package, is used to arrange the Text, CheckBox, HBox, and Button nodes in a column. This
snippet also shows that layout containers may be nested, as demonstrated by the HBox with the name
titleBox that arranges the Label and TextField nodes horizontally. Note that several lines of code are
omitted from this snippet to show the layout nesting clearly:
HBox titleBox = new HBox(titleLabel, titleTextField);
VBox contentBox = new VBox(
        textStageX, textStageY, textStageW, textStageH, textStageF,
        checkBoxResizable, checkBoxFullScreen,
        titleBox, toBackButton, toFrontButton, closeButton);
The VBox layout class is similar to the Group class discussed in the Hello Earthrise example in Chapter 1,
in that it contains a collection of nodes within it. Unlike the Group class, the VBox class arranges its contained
nodes vertically, spacing them apart from each other by the number of pixels specified in the spacing
property.

Ascertaining Whether the Stage Is in Focus
To know whether your JavaFX application is the one that currently is in focus (e.g., keys pressed are delivered
to the application), simply consult the focused property of the Stage instance. The following snippet from
Listing 2-1 demonstrates this.
textStageF.textProperty().bind(new SimpleStringProperty("focused: ")
        .concat(stageRef.focusedProperty().asString()));

Controlling the Z-Order of the Stage
In the event that you want your JavaFX application to appear on top of other windows or behind other
windows onscreen, you can use the toFront() and toBack() methods, respectively. The following snippet
from Listing 2-1 shows how this is accomplished.
Button toBackButton = new Button("toBack()");
toBackButton.setOnAction(e -> stageRef.toBack());
Button toFrontButton = new Button("toFront()");
toFrontButton.setOnAction(e -> stageRef.toFront());
Once again, note how using lambda expressions enhances the readability of the code. It is clear from
the first line of the snippet that a Button named toBackButton is created with a text "toBack()" being
displayed on the button. The second line defines that when an action is performed on the button (i.e., the
button is clicked), the stage is sent to the back.

45

Chapter 2 ■ Creating a User Interface in JavaFX

Without using a lambda expression, the second line would be replaced by a call to an anonymous inner
class as follows:
toBackButton.setOnAction(new EventHandler() {
  @Override public void handle(javafx.event.ActionEvent e) {
    stageRef.toBack();
  }
})
This approach not only requires more code, it doesn’t allow the Java runtime to optimize calls and it is
much less readable.

Closing the Stage and Detecting When It Is Closed
As shown in the following code snippet from Listing 2-1, you can programmatically close the Stage with its
close() method. This is important when the stageStyle is undecorated or transparent, because the close
button supplied by the windowing system is not present.
Button closeButton = new Button("close()");
closeButton.setOnAction(e -> stageRef.close());
By the way, you can detect when there is an external request to close the Stage by using the
onCloseRequest event handler as shown in the following code snippet from Listing 2-1.
stage.setOnCloseRequest((WindowEvent we) -> {
        System.out.println("Stage is closing");
});
To see this in action, run the application without any arguments so that it has the appearance of
Figure 2-2 shown previously, and then click the close button on the decoration of the window.

■■Tip The onCloseRequest event handler is only called when there is an external request to close the
window. This is why the “Stage is closing” message doesn’t appear in this example when you click the button
labeled “close()”.

Making a Scene
Continuing on with our theater metaphor for creating JavaFX applications, we now discuss putting a Scene
on the Stage. The Scene, as you recall, is the place in which the actors and props (nodes) visually interact
with each other and the audience (the users of your program).

Using the Scene Class: The OnTheScene Example
As with the Stage class, we’re going to use a contrived example application to demonstrate and teach the
details of the available capabilities in the Scene class. See Figure 2-6 for a screenshot of the OnTheScene
program.

46

Chapter 2 ■ Creating a User Interface in JavaFX

Figure 2-6. The OnTheScene program when first invoked
Go ahead and run the OnTheScene program, putting it through its paces as instructed in the following
exercise. We follow up with a walkthrough of the code so that you can associate the behavior with the code
behind it.

EXAMINING THE BEHAVIOR OF THE ONTHESCENE PROGRAM
When the OnTheScene program starts, its appearance should be similar to the screenshot in Figure 2-6.
To fully examine its behavior, perform the following steps. Note that the property and method names
on the UI correspond to the property and methods in the Scene, Stage, and Cursor classes, as well as
Cascading Style Sheets (CSS) file names.
1.

Drag the application around, noticing that although the Stage x and y values are
relative to the screen, the Scene’s x and y values are relative to the upper-left
corner of the exterior of the Stage (including decorations). Similarly, the width and
height of the Scene are the dimensions of the interior of the Stage (which doesn’t
include decorations). As noted earlier, it is best to set the Scene width and height
explicitly (or let them be set implicitly by assuming the size of the contained nodes),
rather than setting the width and height of a decorated Stage.

2.

Resize the program’s window and observe that the width and height values change
to reflect the width and height of the Scene. Also notice that the position of much of
the content in the scene changes as you change the height of the window.

3.

Click the lookup( ) hyperlink and notice that the string “Scene height: XXX.X” prints
in the console, where XXX.X is the Scene’s height.

4.

Hover the mouse over the choice box drop-down list and notice that it becomes
slightly larger. Click the choice box and choose a cursor style in the list, noticing
that the cursor changes to that style. Be careful about choosing NONE, as the
cursor might disappear, and you’ll need to use the keyboard (or psychic powers
while moving the mouse) to make it visible.

47

Chapter 2 ■ Creating a User Interface in JavaFX

5.

Drag the slider on the left, noticing that the fill color of the Scene changes and that
the string at the top of the Scene reflects the red-green-blue (RGB) and opacity
values of the current fill color.

6.

Notice the appearance and content of the text on the Scene. Then click
changeOfScene.css, noticing that the color and font and content characteristics for
some of the text on the Scene changes as shown in the screenshot in Figure 2-7.

Figure 2-7. The OnTheScene program with the changeOfScene CSS style sheet applied

7.

Click OnTheScene.css, noticing that the color and font characteristics return to their
previous state.

Now that you've explored this example program that demonstrates features of the Scene, let’s walk
through the code!

Understanding the OnTheScene Program
Take a look at the code for the OnTheScene program in Listing 2-2, before we point out new and relevant
concepts.
Listing 2-2. OnTheSceneMain.java
import
import
import
import
import
import
import
import
import
import
import
import
import

48

javafx.application.Application;
javafx.beans.property.DoubleProperty;
javafx.beans.property.SimpleDoubleProperty;
javafx.beans.property.SimpleStringProperty;
javafx.collections.FXCollections;
javafx.collections.ObservableList;
javafx.geometry.HPos;
javafx.geometry.Insets;
javafx.geometry.Orientation;
javafx.geometry.VPos;
javafx.scene.Cursor;
javafx.scene.Scene;
javafx.scene.control.ChoiceBox;

Chapter 2 ■ Creating a User Interface in JavaFX

import
import
import
import
import
import
import
import
import
import
import
import

javafx.scene.control.Hyperlink;
javafx.scene.control.Label;
javafx.scene.control.RadioButton;
javafx.scene.control.Slider;
javafx.scene.control.ToggleGroup;
javafx.scene.layout.FlowPane;
javafx.scene.layout.HBox;
javafx.scene.paint.Color;
javafx.scene.text.Font;
javafx.scene.text.FontWeight;
javafx.scene.text.Text;
javafx.stage.Stage;

public class OnTheSceneMain extends Application {
    DoubleProperty fillVals = new SimpleDoubleProperty(255.0);
    Scene sceneRef;
    ObservableList cursors = FXCollections.observableArrayList(
            Cursor.DEFAULT,
            Cursor.CROSSHAIR,
            Cursor.WAIT,
            Cursor.TEXT,
            Cursor.HAND,
            Cursor.MOVE,
            Cursor.N_RESIZE,
            Cursor.NE_RESIZE,
            Cursor.E_RESIZE,
            Cursor.SE_RESIZE,
            Cursor.S_RESIZE,
            Cursor.SW_RESIZE,
            Cursor.W_RESIZE,
            Cursor.NW_RESIZE,
            Cursor.NONE
    );
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        Slider sliderRef;
        ChoiceBox choiceBoxRef;
        Text textSceneX;
        Text textSceneY;
        Text textSceneW;
        Text textSceneH;
        Label labelStageX;
        Label labelStageY;

49

Chapter 2 ■ Creating a User Interface in JavaFX

        Label labelStageW;
        Label labelStageH;
        final ToggleGroup toggleGrp = new ToggleGroup();
        sliderRef = new Slider(0, 255, 255);
        sliderRef.setOrientation(Orientation.VERTICAL);
        choiceBoxRef = new ChoiceBox(cursors);
        HBox hbox = new HBox(sliderRef, choiceBoxRef);
        hbox.setSpacing(10);
        textSceneX = new Text();
        textSceneX.getStyleClass().add("emphasized-text");
        textSceneY = new Text();
        textSceneY.getStyleClass().add("emphasized-text");
        textSceneW = new Text();
        textSceneW.getStyleClass().add("emphasized-text");
        textSceneH = new Text();
        textSceneH.getStyleClass().add("emphasized-text");
        textSceneH.setId("sceneHeightText");
        Hyperlink hyperlink = new Hyperlink("lookup");
        hyperlink.setOnAction((javafx.event.ActionEvent e) -> {
            System.out.println("sceneRef:" + sceneRef);
            Text textRef = (Text) sceneRef.lookup("#sceneHeightText");
            System.out.println(textRef.getText());
        });
        RadioButton radio1 = new RadioButton("onTheScene.css");
        radio1.setSelected(true);
        radio1.setToggleGroup(toggleGrp);
        RadioButton radio2 = new RadioButton("changeOfScene.css");
        radio2.setToggleGroup(toggleGrp);
        labelStageX = new Label();
        labelStageX.setId("stageX");
        labelStageY = new Label();
        labelStageY.setId("stageY");
        labelStageW = new Label();
        labelStageH = new Label();
        FlowPane sceneRoot = new FlowPane(Orientation.VERTICAL, 20, 10, hbox,
                textSceneX, textSceneY, textSceneW, textSceneH, hyperlink,
                radio1, radio2,
                labelStageX, labelStageY,
                labelStageW,
                labelStageH);
        sceneRoot.setPadding(new Insets(0, 20, 40, 0));
        sceneRoot.setColumnHalignment(HPos.LEFT);
        sceneRoot.setLayoutX(20);
        sceneRoot.setLayoutY(40);
        sceneRef = new Scene(sceneRoot, 600, 250);
        sceneRef.getStylesheets().add("onTheScene.css");
        stage.setScene(sceneRef);

50

Chapter 2 ■ Creating a User Interface in JavaFX

        choiceBoxRef.getSelectionModel().selectFirst();
        // Setup various property binding
        textSceneX.textProperty().bind(new SimpleStringProperty("Scene x: ")
                .concat(sceneRef.xProperty().asString()));
        textSceneY.textProperty().bind(new SimpleStringProperty("Scene y: ")
                .concat(sceneRef.yProperty().asString()));
        textSceneW.textProperty().bind(new SimpleStringProperty("Scene width: ")
                .concat(sceneRef.widthProperty().asString()));
        textSceneH.textProperty().bind(new SimpleStringProperty("Scene height: ")
                .concat(sceneRef.heightProperty().asString()));
        labelStageX.textProperty().bind(new SimpleStringProperty("Stage x: ")
                .concat(sceneRef.getWindow().xProperty().asString()));
        labelStageY.textProperty().bind(new SimpleStringProperty("Stage y: ")
                .concat(sceneRef.getWindow().yProperty().asString()));
        labelStageW.textProperty().bind(new SimpleStringProperty("Stage width: ")
                .concat(sceneRef.getWindow().widthProperty().asString()));
        labelStageH.textProperty().bind(new SimpleStringProperty("Stage height: ")
                .concat(sceneRef.getWindow().heightProperty().asString()));
        sceneRef.cursorProperty().bind(choiceBoxRef.getSelectionModel()
                .selectedItemProperty());
        fillVals.bind(sliderRef.valueProperty());
        // When fillVals changes, use that value as the RGB to fill the scene
        fillVals.addListener((ov, oldValue, newValue) -> {
            Double fillValue = fillVals.getValue() / 256.0;
            sceneRef.setFill(new Color(fillValue, fillValue, fillValue, 1.0));
        });
        // When the selected radio button changes, set the appropriate style sheet
        toggleGrp.selectedToggleProperty().addListener((ov, oldValue, newValue) -> {
            String radioButtonText = ((RadioButton) toggleGrp.getSelectedToggle())
                    .getText();
            sceneRef.getStylesheets().clear();
            sceneRef.getStylesheets().addAll(radioButtonText);
        });
        stage.setTitle("On the Scene");
        stage.show();
        // Define an unmanaged node that will display Text
        Text addedTextRef = new Text(0, -30, "");
        addedTextRef.setTextOrigin(VPos.TOP);
        addedTextRef.setFill(Color.BLUE);
        addedTextRef.setFont(Font.font("Sans Serif", FontWeight.BOLD, 16));
        addedTextRef.setManaged(false);
        // Bind the text of the added Text node to the fill property of the Scene
        addedTextRef.textProperty().bind(new SimpleStringProperty("Scene fill: ").
                concat(sceneRef.fillProperty()));

51

Chapter 2 ■ Creating a User Interface in JavaFX

        // Add to the Text node to the FlowPane
        ((FlowPane) sceneRef.getRoot()).getChildren().add(addedTextRef);
    }
}

Setting the Cursor for the Scene
The cursor can be set for a given node, for the entire scene, or for both. To do the latter, set the cursor
property of the Scene instance to one of the constant values in the Cursor class, as shown in the following
snippet from Listing 2-2.
sceneRef.cursorProperty().bind(choiceBoxRef.getSelectionModel()
        .selectedItemProperty());
These cursor values can be seen by looking at the javafx.scene.Cursor class in the JavaFX API docs;
we've created a collection of these constants in Listing 2-2.

Painting the Scene’s Background
The Scene class has a fill property of type javafx.scene.paint.Paint. Looking at the JavaFX API will
reveal that the known subclasses of Paint are Color, ImagePattern, LinearGradient, and RadialGradient.
Therefore, a Scene’s background can be filled with solid colors, patterns, and gradients. If you don’t set the
fill property of the Scene, the default color (white) will be used.

■■Tip One of the Color constants is Color.TRANSPARENT, so you may make the Scene’s background
completely transparent if desired. In fact, the reason that the Scene behind the rounded-cornered rectangle in
the StageCoach screenshot in Figure 2-5 isn’t white is that its fill property is set to Color.TRANSPARENT
(see Listing 2-1).
To set the fill property in the OnTheScene example, instead of using one of the constants in the Color
class (e.g., Color.BLUE), we’re using an RGB formula to create the color. Take a look at the javafx.scene.
paint.Color class in the JavaFX API docs and scroll down past the constants such as ALICEBLUE and
WHITESMOKE to see the constructors and methods. We’re using a constructor of the Color class, setting the fill
property to it, as shown in the following snippet from Listing 2-2.
sceneRef.setFill(new Color(fillValue, fillValue, fillValue, 1.0));
As you move the Slider, to which the fillVals property is bound, each of the arguments to the Color()
constructor is set to a value from 0 to 255, as indicated in the following code snippet from Listing 2-2.
fillVals.bind(sliderRef.valueProperty());

Populating the Scene with Nodes
As covered in Chapter 1, you can populate a Scene with nodes by instantiating them and adding them
to container nodes (e.g., Group and VBox) that can contain other nodes. These capabilities enable you to
construct complex scene graphs containing nodes. In the example here, the root property of the Scene

52

Chapter 2 ■ Creating a User Interface in JavaFX

contains a Flow layout container, which causes its contents to flow either vertically or horizontally,
wrapping as necessary. The Flow container in our example contains an HBox (which contains a Slider and a
ChoiceBox) and several other nodes (instances of Text, Hyperlink, and RadioButton classes).

Finding a Scene Node by ID
Each node in a Scene can be assigned an ID in the id property of the node. For example, in the following
snippet from Listing 2-2, the id property of a Text node is assigned the String "sceneHeightText". When
the action event handler in the Hyperlink control is called, the lookup() method of the Scene instance is
used to obtain a reference to the node with the id of "sceneHeightText". The event handler then prints the
content of the Text node to the console.

■■Note The Hyperlink control is essentially a button that has the appearance of hyperlink text. It has
an action event handler in which you could place code that opens a browser page or any other desired
functionality.

textSceneH = new Text();
textSceneH.getStyleClass().add("emphasized-text");
textSceneH.setId("sceneHeightText");
Hyperlink hyperlink = new Hyperlink("lookup");
hyperlink.setOnAction((javafx.event.ActionEvent e) -> {
    System.out.println("sceneRef:" + sceneRef);
    Text textRef = (Text) sceneRef.lookup("#sceneHeightText");
    System.out.println(textRef.getText());
});
A close examination of the action event handler reveals that the lookup() method returns a Node, but
the actual type of object returned in this snippet is a Text object. Because we need to access a property of the
Text class (text) that isn’t in the Node class, it is necessary to coerce the compiler into trusting that at runtime
the object will be an instance of the Text class.

Accessing the Stage from the Scene
To obtain a reference to the Stage instance from the Scene, we use a property in the Scene class named
window. The accessor method for this property appears in the following snippet from Listing 2-2 to get the x
and y co-ordinates of the Stage on the screen.
labelStageX.textProperty().bind(new SimpleStringProperty("Stage x: ")
        .concat(sceneRef.getWindow().xProperty().asString()));
labelStageY.textProperty().bind(new SimpleStringProperty("Stage y: ")
        .concat(sceneRef.getWindow().yProperty().asString()));

53

Chapter 2 ■ Creating a User Interface in JavaFX

Inserting a Node into the Scene’s Content Sequence
Sometimes it is useful to add a node dynamically to the children of a UI container class. The code snippet
from Listing 2-2 that follows demonstrates how this may be accomplished by dynamically adding a Text
node to the children of the FlowPane instance:
// Define an unmanaged node that will display Text
Text addedTextRef = new Text(0, -30, "");
addedTextRef.setTextOrigin(VPos.TOP);
addedTextRef.setFill(Color.BLUE);
addedTextRef.setFont(Font.font("Sans Serif", FontWeight.BOLD, 16));
addedTextRef.setManaged(false);
// Bind the text of the added Text node to the fill property of the Scene
addedTextRef.textProperty().bind(new SimpleStringProperty("Scene fill: ").
        concat(sceneRef.fillProperty()));
// Add the Text node to the FlowPane
((FlowPane) sceneRef.getRoot()).getChildren().add(addedTextRef);
This particular Text node is the one at the top of the Scene shown in Figures 2-6 and 2-7, in which
the value of the Scene’s fill property is displayed. Note that in this example the managed property of the
addedTextRef instance is set to false, so its position isn’t governed by the FlowPane. By default, nodes are
“managed,” which means that their parent (the container to which this node is added) is responsible for the
layout of the node. By setting the managed property to false, the developer is assumed to be responsible for
laying out the node.

CSS Styling the Nodes in a Scene
A very powerful aspect of JavaFX is the ability to use CSS to style the nodes in a Scene dynamically. You
used this capability in step 6 of the previous exercise when you clicked changeOfScene.css to change the
appearance of the UI from what you saw in Figure 2-6 to what was shown in Figure 2-7. Also, in step 7 of the
exercise, the appearance of the UI changed back to what was shown in Figure 2-6 when you selected the
onTheScene.css radio button. The relevant code snippet from Listing 2-2 is shown here:
sceneRef.getStylesheets().add("onTheScene.css");
...code omitted...
// When the selected radio button changes, set the appropriate stylesheet
        toggleGrp.selectedToggleProperty().addListener((ov, oldValue, newValue) -> {
        String radioButtonText = ((RadioButton) toggleGrp.getSelectedToggle())
                .getText();
        sceneRef.getStylesheets().clear();
        sceneRef.getStylesheets().addAll("/"+radioButtonText);
});
In this snippet, the stylesheets property of the Scene is initialized to the location of the onTheScene.
css file, which in this case is the root directory. Also shown in the snippet is the assignment of the CSS files to
the Scene as the appropriate buttons are clicked. The text of the RadioButton instances is equal to the names
of the style sheets, hence we can easily set the corresponding style sheet to the scene. Take a look at Listing
2-3 to see the style sheet that corresponds to the screenshot in Figure 2-6. Some of the CSS selectors in this
style sheet represent the nodes whose id property is either "stageX" or "stageY". There is also a selector in

54

Chapter 2 ■ Creating a User Interface in JavaFX

this style sheet that represents nodes whose styleClass property is "emphasized-text". In addition, there
is a selector in this style sheet that maps to the ChoiceBox UI control by substituting the camel-case name of
the control to a lowercase hyphenated name (choice-box). The properties in this style sheet begin with "-fx",
and correspond to the type of node with which they are associated. The values in this style sheet (e.g., black,
italic, and 14pt) are expressed as standard CSS values.
Listing 2-3. onTheScene.css
#stageX, #stageY {
  -fx-padding: 1;
  -fx-border-color: black;
  -fx-border-style: dashed;
  -fx-border-width: 2;
  -fx-border-radius: 5;
}
.emphasized-text {
  -fx-font-size: 14pt;
  -fx-font-weight: normal;
  -fx-font-style: italic;
}
.choice-box:hover {
    -fx-scale-x: 1.1;
    -fx-scale-y: 1.1;
}
.radio-button .radio  {
   -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border,
                         -fx-inner-border, -fx-body-color;
   -fx-background-insets: 0 0 -1 0,  0,  1,  2;
   -fx-background-radius: 1.0em;
   -fx-padding: 0.333333em;
}
.radio-button:focused .radio {
    -fx-background-color: -fx-focus-color, -fx-outer-border,
                          -fx-inner-border, -fx-body-color;
    -fx-background-radius: 1.0em;
    -fx-background-insets: -1.4, 0, 1, 2;
}
Listing 2-4 is the style sheet that corresponds to the screenshot in Figure 2-7. For more information on
CSS style sheets, see the “Resources” section at the end of this chapter.
Listing 2-4. changeOfScene.css
#stageX, #stageY {
  -fx-padding: 3;
  -fx-border-color: blue;
  -fx-stroke-dash-array: 12 2 4 2;
  -fx-border-width: 4;

55

Chapter 2 ■ Creating a User Interface in JavaFX

  -fx-border-radius: 5;
}
.emphasized-text {
  -fx-font-size: 14pt;
  -fx-font-weight: bold;
  -fx-font-style: normal;
}
.radio-button *.radio  {
    -fx-padding: 10;
    -fx-background-color: red, yellow;
    -fx-background-insets: 0, 5;
    -fx-background-radius: 30, 20;
}
.radio-button:focused *.radio {
    -fx-background-color: blue, red, yellow;
    -fx-background-insets: -5, 0, 5;
    -fx-background-radius: 40, 30, 20;
}
Now that you've had some experience with using the Stage and Scene classes, several of the Node
subclasses, and CSS styling, we show you how to handle events that can occur when your JavaFX program is
running.

Handling Input Events
So far we've shown you a couple of examples of event handling. For example, we used the onAction event
handler to execute code when a button is clicked. We also used the onCloseRequest event handler of the
Stage class to execute code when the Stage has been requested externally to close. In this section, we
explore more of the event handlers available in JavaFX.

Surveying Mouse, Keyboard, Touch, and Gesture Events and
Handlers
Most of the events that occur in JavaFX programs are related to the user manipulating input devices such as
a mouse, a keyboard, or a multitouch screen. To see the available event handlers and their associated event
objects, we take yet another look at the JavaFX API documentation. First, navigate to the javafx.scene.
Node class and look for the properties that begin with the letters “on”. These properties represent the event
handlers common to all nodes in JavaFX. Here is a list of these event handlers in the JavaFX 8 API:

56

•

Key event handlers: onKeyPressed, onKeyReleased, onKeyTyped

•

Mouse event handlers: onMouseClicked, onMouseDragEntered, onMouseDragExited,
onMouseDragged, onMouseDragOver, onMouseDragReleased, onMouseEntered,
onMouseExited, onMouseMoved, onMousePressed, onMouseReleased

•

Drag-and-drop handlers: onDragDetected, onDragDone, onDragDropped,
onDragEntered, onDragExited, onDragOver

Chapter 2 ■ Creating a User Interface in JavaFX

•

Touch handlers: onTouchMoved, onTouchPressed, onTouchReleased,
onTouchStationary

•

Gesture handlers: onRotate, onRotationFinished, onRotationStarted, onScroll,
onScrollStarted, onScrollFinished, onSwipeLeft, onSwipeRight, onSwipeUp,
onSwipeDown, onZoom, onZoomStarted, onZoomFinished

Each of these is a property that defines a method to be called when particular input events occur. In
the case of the key event handlers, as shown in the JavaFX API docs, the method’s parameter is a javafx.
scene.input.KeyEvent instance. The method’s parameter for the mouse event handlers is a javafx.scene.
input.MouseEvent. Touch handlers consume a javafx.scene.input.TouchEvent instance, and when a
gesture event occurs, the method’s parameter for the handle event is an instance of javax.scene.input.
GestureInput.

Understanding the KeyEvent Class
Take a look at the JavaFX API docs for the KeyEvent class, and you’ll see that it contains several methods, a
commonly used one being getCode(). The getCode() method returns a KeyCode instance representing the
key that caused the event when pressed. Looking at the javafx.scene.input.KeyCode class in the JavaFX
API docs reveals that a multitude of constants exist that represent keys on an international set of keyboards.
Another way to find out what key was pressed is to call the getCharacter() method, which returns a string
that represents the Unicode character associated with the key pressed.
The KeyEvent class also enables you to see whether the Alt, Ctrl, Meta, and/or Shift keys were down
at the time of the event by calling the isAltDown(), isControlDown(), isMetaDown(), or isShiftDown()
methods, respectively.

Understanding the MouseEvent Class
Take a look at the MouseEvent class in the JavaFX API docs, and you see that significantly more methods
are available than in KeyEvent. Like KeyEvent, MouseEvent has the isAltDown(), isControlDown(),
isMetaDown(), and isShiftDown() methods, as well as the source field, which is a reference to the object
in which the event originated. In addition, it has several methods that pinpoint various coordinate spaces
where the mouse event occurred, all expressed in pixels:
•

getX() and getY() return the horizontal and vertical position of the mouse event,
relative to the origin of the node in which the mouse event occurred.

•

getSceneX() and getSceneY() return the horizontal and vertical position of the
mouse event, relative to the Scene.

•

getScreenX() and getScreenY() return the horizontal and vertical position of the
mouse event, relative to the screen.

Here are a few other commonly useful methods:
•

isDragDetect() returns true if a drag event is detected.

•

getButton(), isPrimaryButtonDown(), isSecondaryButtonDown(),
isMiddleButtonDown(), and getClickCount() contain information about what
button was clicked, and how many times it was clicked.

A little later in this chapter you get some experience with creating key and mouse event handlers in the
ZenPong example program. To continue preparing you for the ZenPong example, we now give you a look at
how you can animate the nodes that you put in your scene.

57

Chapter 2 ■ Creating a User Interface in JavaFX

Understanding the TouchEvent Class
With more and more devices being equipped with a touch screen, built-in support for touch events makes
JavaFX a state-of-the art platform for creating applications that leverage multitouch capabilities, by which we
mean that the platform is able to track more than one touchpoint in a single set of events.
The TouchEvent class provides the getTouchPoint() method, which returns a specific touch point.
The methods on this TouchPoint are similar to the methods on a MouseEvent, for example, you can retrieve
the relative and absolute positions by calling getX() and getY(), or getSceneX() and getSceneY(), or
getScreenX() and getScreenY().
The TouchEvent class also allows the developer to get information about the other touch points that
belong to the same set. By calling getEventSetId(), you get the unique identifier of the set of TouchEvent
instances, and the list of all touch points in the set can be obtained by calling getTouchPoints(), which
returns a list of TouchPoint instances.

Understanding the GestureEvent Class
Besides handling multitouch events, JavaFX also supports the creation and dispatching of gesture events.
Gestures are increasingly used on smartphones, tablets, touch screens, and other input devices. They
provide an intuitive way of performing an action, for example, by having the user swipe his or her finger.
The GestureEvent class currently has four subclasses, each representing a specific gesture: RotateEvent,
ScrollEvent, SwipeEvent, and ZoomEvent. All of these events have methods comparable to the MouseEvent
for retrieving the position of the action—the getX() and getY(), getSceneX() and getSceneY(), and the
getScreenX() and getScreenY() methods.
The specific subclasses all allow for retrieving a more detailed type of the event. A SwipeEvent, for
example can be a swipe to the right or the left, to the top or the bottom. This information is obtained by
calling the getEventType() method on the GestureEvent.

Animating Nodes in the Scene
One of the strengths of JavaFX is the ease with which you can create graphically rich UIs. Part of that richness
is the ability to animate nodes that live in the Scene. At its core, animating a node involves changing the
value of its properties over a period of time. Examples of animating a node include the following.
•

Gradually increasing the size of a node when the mouse enters its bounds, and
gradually decreasing the size when the mouse exits its bounds. Note that this
requires scaling the node, which is referred to as a transform.

•

Gradually increasing or decreasing the opacity of a node to provide a fade-in or fadeout effect, respectively.

•

Gradually altering values of properties in a node that change its location, causing it to
move from one location to another. This is useful, for example, when creating a game such
as Pong. A related capability is detecting when a node has collided with another node.

Animating a node involves the use of the Timeline class, located in the javafx.animation package.
Depending on the requirements of an animation and personal preference, use one of two general techniques:

58

•

Create an instance of the Timeline class directly and supply key frames that specify
values and actions at specific points in time.

•

Use the javafx.animation.Transition subclasses to define and associate specific
transitions with a node. Examples of transitions include causing a node to move
along a defined path over a period of time, and rotating a node over a period of time.
Each of these transition classes extends the Timeline class.

Chapter 2 ■ Creating a User Interface in JavaFX

We now cover these techniques, showing examples of each, beginning with the first one listed.

Using a Timeline for Animation
Take a look at the javafx.animation package in the JavaFX API docs, and you see three of the classes that
are used when directly creating a timeline: Timeline, KeyFrame, and Interpolator. Peruse the docs for these
classes, and then come back so we can show you some examples of using them.

■■Tip Remember to consult the JavaFX API docs for any new packages, classes, properties, and methods
that you encounter.

The Metronome1 Example
We use a simple metronome example to demonstrate how to create a timeline.
As the screenshot in Figure 2-8 shows, the Metronome1 program has a pendulum as well as four buttons
that start, pause, resume, and stop the animation. The pendulum in this example is a Line node, and we’re
going to animate that node by interpolating its startX property over the period of one second. Go ahead and
take this example for a spin by doing the following exercise.

Figure 2-8. The Metronome1 program

59

Chapter 2 ■ Creating a User Interface in JavaFX

EXAMINING THE BEHAVIOR OF THE METRONOME1 PROGRAM
When the Metronome1 program starts, its appearance should be similar to the screenshot in Figure 2-8.
To fully examine its behavior, perform the following steps.
1.

Observe that of the four buttons on the scene, only the Start button is enabled.

2.

Click Start. Notice that the top of the line moves back and forth, taking one second
to travel each direction. Also, observe that the Start and Resume buttons are
disabled and that the Pause and Stop buttons are enabled.

3.

Click Pause, noticing that the animation pauses. Also, observe that the Start and
Pause buttons are disabled and that the Resume and Stop buttons are enabled.

4.

Click Resume, noticing that the animation resumes from where it was paused.

5.

Click Stop, noticing that the animation stops and that the button states are the
same as they were when the program was first started (see step 1).

6.

Click Start again, noticing that the line jumps back to its starting point before
beginning the animation (rather than simply resuming as it did in step 4).

7.

Click Stop.

Now that you've experienced the behavior of the Metronome1 program, let’s walk through the code
behind it.

Understanding the Metronome1 Program
Take a look at the code for the Metronome1 program in Listing 2-5, before we discuss relevant concepts.
Listing 2-5. Metronome1Main.java
package projavafx.metronome1.ui;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

60

javafx.animation.Animation;
javafx.animation.Interpolator;
javafx.animation.KeyFrame;
javafx.animation.KeyValue;
javafx.animation.Timeline;
javafx.application.Application;
javafx.beans.property.DoubleProperty;
javafx.beans.property.SimpleDoubleProperty;
javafx.scene.Group;
javafx.scene.Scene;
javafx.scene.control.Button;
javafx.scene.layout.HBox;
javafx.scene.paint.Color;
javafx.scene.shape.Line;
javafx.stage.Stage;
javafx.util.Duration;

Chapter 2 ■ Creating a User Interface in JavaFX

public class Metronome1Main extends Application {
    DoubleProperty startXVal = new SimpleDoubleProperty(100.0);
    Button startButton;
    Button pauseButton;
    Button resumeButton;
    Button stopButton;
    Line line;
    Timeline anim;
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        anim = new Timeline(
                new KeyFrame(new Duration(0.0), new KeyValue(startXVal, 100.)),
                new KeyFrame(new Duration(1000.0), new KeyValue(startXVal, 300.,
Interpolator.LINEAR))
        );
        anim.setAutoReverse(true);
        anim.setCycleCount(Animation.INDEFINITE);
        line = new Line(0, 50, 200, 400);
        line.setStrokeWidth(4);
        line.setStroke(Color.BLUE);
        startButton = new Button("start");
        startButton.setOnAction(e -> anim.playFromStart());
        pauseButton = new Button("pause");
        pauseButton.setOnAction(e -> anim.pause());
        resumeButton = new Button("resume");
        resumeButton.setOnAction(e -> anim.play());
        stopButton = new Button("stop");
        stopButton.setOnAction(e -> anim.stop());
        HBox commands = new HBox(10,
                startButton,
                pauseButton,
                resumeButton,
                stopButton);
        commands.setLayoutX(60);
        commands.setLayoutY(420);
        Group group = new Group(line, commands);
        Scene scene = new Scene(group, 400, 500);
        line.startXProperty().bind(startXVal);
        startButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.STOPPED));
        pauseButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.RUNNING));
        resumeButton.disableProperty().bind(anim.statusProperty()

61

Chapter 2 ■ Creating a User Interface in JavaFX

                .isNotEqualTo(Animation.Status.PAUSED));
        stopButton.disableProperty().bind(anim.statusProperty()
                .isEqualTo(Animation.Status.STOPPED));
        stage.setScene(scene);
        stage.setTitle("Metronome 1");
        stage.show();
    }
}

Understanding the Timeline Class
The main purpose of the Timeline class is to provide the ability to change the values of properties in a
gradual fashion over given periods of time. Take a look at the following snippet from Listing 2-5 to see the
timeline being created, along with some of its commonly used properties.
DoubleProperty startXVal = new SimpleDoubleProperty(100.0);
  ...code omitted...
Timeline anim = new Timeline(
                new KeyFrame(new Duration(0.0), new KeyValue(startXVal, 100.)),
                new KeyFrame(new Duration(1000.0), new KeyValue(startXVal, 300.,
Interpolator.LINEAR))
        );
anim.setAutoReverse(true);
        anim.setCycleCount(Animation.INDEFINITE);
  ...code omitted...
line = new Line(0, 50, 200, 400);
        line.setStrokeWidth(4);
        line.setStroke(Color.BLUE);
  ...code omitted...
    line.startXProperty().bind(startXVal);

■■Note In JavaFX 2, it was recommended to use the builder pattern for creating Nodes. As a consequence,
creating a Line would be done as follows:

line = LineBuilder.create()
.startY(50)
.endX(200)

62

Chapter 2 ■ Creating a User Interface in JavaFX

.endY(400)
.strokeWidth(4)
.stroke(Color.BLUE)
.build();

The advantage of this approach is that it is clear what the parameter “50” in the second line means: The line
has a start-coordinate of 50 in the vertical position. The same readability can be achieved by calling setter
methods, for example
line.setStartY(50);

In practice, however, many parameters are passed via the constructor of the Node. In the case of a Line
instance, the second parameter is the startY parameter. This approach leads to fewer lines of code, but the
developer should be careful about the order and the meaning of the parameters in the constructor. Once again,
we strongly recommend having the Javadoc available while writing JavaFX applications.

Inserting Key Frames into the Timeline
Our timeline contains a collection of two KeyFrame instances. Using the KeyValue constructor, one of these
instances assigns 100 to the startXVal property at the beginning of the timeline, and the other assigns 300 to
the startXVal property when the timeline has been running for one second. Because the startX property of
the Line is bound to the value of the startXVal property, the net result is that the top of the line moves 200
pixels horizontally over the course of one second.
In the second KeyFrame of the timeline, the KeyValue constructor is passed a third argument that
specifies that the interpolation from 100 to 300 will occur in a linear fashion over the one-second duration.
Other Interpolation constants include EASE_IN, EASE_OUT, and EASE_BOTH. These cause the interpolation in
a KeyFrame to be slower in the beginning, ending, or both, respectively.
The following are the other Timeline properties, inherited from the Animation class, used in this
example:
•

autoReverse, which we’re initializing to true. This causes the timeline to
automatically reverse when it reaches the last KeyFrame. When reversed, the
interpolation goes from 300 to 100 over the course of one second.

•

cycleCount, which we’re initializing to Animation.INDEFINITE. This causes the
timeline to repeat indefinitely until stopped by the stop() method of the Timeline
class.

Speaking of the methods of the Timeline class, now is a good time to show you how to control the
timeline and monitor its state.

Controlling and Monitoring the Timeline
As you observed when using the Metronome1 program, clicking the buttons causes the animation to start,
pause, resume, and stop. This in turn has an effect on the states of the animation (running, paused, or
stopped). Those states are reflected in the buttons in the form of being enabled or disabled. The following

63

Chapter 2 ■ Creating a User Interface in JavaFX

snippet from Listing 2-5 shows how to start, pause, resume, and stop the timeline, as well as how to tell
whether the timeline is running or paused.
startButton = new Button("start");
startButton.setOnAction(e -> anim.playFromStart());
pauseButton = new Button("pause");
pauseButton.setOnAction(e -> anim.pause());
resumeButton = new Button("resume");
resumeButton.setOnAction(e -> anim.play());
stopButton = new Button("stop");
stopButton.setOnAction(e -> anim.stop());
...code omitted...
startButton.disableProperty().bind(anim.statusProperty()
        .isNotEqualTo(Animation.Status.STOPPED));
pauseButton.disableProperty().bind(anim.statusProperty()
        .isNotEqualTo(Animation.Status.RUNNING));
resumeButton.disableProperty().bind(anim.statusProperty()
        .isNotEqualTo(Animation.Status.PAUSED));
stopButton.disableProperty().bind(anim.statusProperty()
        .isEqualTo(Animation.Status.STOPPED));
As shown here in the action event handler of the Start button, the playFromStart() method of the
Timeline instance is called, which begins playing the timeline from the beginning. In addition, the disable
property of that Button is bound to an expression that evaluates whether the status property of the timeline
is not equal to Animation.Status.STOPPED. This causes the button to be disabled when the timeline is not
stopped (in which case it must be either running or paused).
When the user clicks the Pause button, the action event handler calls the timeline’s pause() method,
which pauses the animation. The disable property of that Button is bound to an expression that evaluates
whether the timeline is not running.
The Resume button is only disabled when the timeline is not paused. To resume the timeline from
where it was paused, the action event handler calls the play() method of the timeline.
Finally, the Stop button is disabled when the timeline is stopped. To stop the timeline, the action event
handler calls the stop() method of the timeline.
Now that you know how to animate nodes by creating a Timeline class and creating KeyFrame
instances, it’s time to learn how to use the transition classes to animate nodes.

Using the Transition Classes for Animation
Using a TimeLine allows very flexible animations. There are a number of common animations facilitating the
translation from one state to another that are out-of-the box supported by JavaFX. The javafx.animation
package contains several classes whose purpose is to provide convenient ways to do these commonly used
animation tasks. Both TimeLine and Transition (the abstract root class for all concrete transitions) extend
the Animation class.
Table 2-1 contains a list of transition classes in that package.

64

Chapter 2 ■ Creating a User Interface in JavaFX

Table 2-1. Transition Classes in the javafx.animation Package for Animating Nodes

Transition Class Name

Description

TranslateTransition

Translates (moves) a node from one location to another over a given period of
time. This was employed in the Hello Earthrise example program in Chapter 1.

PathTransition

Moves a node along a specified path.

RotateTransition

Rotates a node over a given period of time.

ScaleTransition

Scales (increases or decreases the size of ) a node over a given period of time.

FadeTransition

Fades (increases or decreases the opacity of ) a node over a given period of time.

FillTransition

Changes the fill of a shape over a given period of time.

StrokeTransition

Changes the stroke color of a shape over a given period of time.

PauseTransition

Executes an action at the end of its duration; designed mainly to be used in a
SequentialTransition as a means to wait for a period of time.

SequentialTransition

Allows you to define a series of transitions that execute sequentially.

ParallelTransition

Allows you to define a series of transitions that execute in parallel.

Let’s take a look at a variation on the metronome theme in which we create a metronome using
TranslateTransition for the animation.

The MetronomeTransition Example
When using the transition classes, we take a different approach toward animation than when using the
Timeline class directly:
•

In the timeline-based Metronome1 program, we bound a property of a node
(specifically, startX) to a property in the model (startXVal), and then used the
timeline to interpolate the value of the property in the model.

•

When using a transition class, however, we assign values to the properties of the
Transition subclass, one of which is a node. The net result is that the node itself is
affected, rather than just a bound attribute of the node being affected.

The distinction between these two approaches becomes clear as we walk through the
MetronomeTransition example. Figure 2-9 shows a screenshot of this program when it is first invoked.

65

Chapter 2 ■ Creating a User Interface in JavaFX

Figure 2-9. The MetronomeTransition program
The first noticeable difference between this example and the previous (Metronome1) example is that
instead of one end of a line moving back and forth, we’re going to make a Circle node move back and forth.

The Behavior of the MetronomeTransition Program
Go ahead and run the program, and perform the same steps that you did in the previous exercise with
Metronome1. Everything should function the same, except for the visual difference pointed out previously.

Understanding the MetronomeTransition Program
Take a look at the code for the MetronomeTransition program in Listing 2-6, before we point out relevant
concepts.
Listing 2-6. MetronomeTransitionMain.fx
package projavafx.metronometransition.ui;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;

66

Chapter 2 ■ Creating a User Interface in JavaFX

import
import
import
import
import
import
import
import
import

javafx.application.Application;
javafx.scene.Group;
javafx.scene.Scene;
javafx.scene.control.Button;
javafx.scene.layout.HBox;
javafx.scene.paint.Color;
javafx.scene.shape.Circle;
javafx.stage.Stage;
javafx.util.Duration;

public class MetronomeTransitionMain extends Application {
    Button
    Button
    Button
    Button
    Circle

startButton;
pauseButton;
resumeButton;
stopButton;
circle;

    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        circle = new Circle(100, 50, 4, Color.BLUE);
        TranslateTransition anim = new TranslateTransition(new Duration(1000.0), circle);
        anim.setFromX(0);
        anim.setToX(200);
        anim.setAutoReverse(true);
        anim.setCycleCount(Animation.INDEFINITE);
        anim.setInterpolator(Interpolator.LINEAR);
        startButton = new Button("start");
        startButton.setOnAction(e -> anim.playFromStart());
        pauseButton = new Button("pause");
        pauseButton.setOnAction(e -> anim.pause());
        resumeButton = new Button("resume");
        resumeButton.setOnAction(e -> anim.play());
        stopButton = new Button("stop");
        stopButton.setOnAction(e -> anim.stop());
        HBox commands = new HBox(10, startButton,
                pauseButton,
                resumeButton,
                stopButton);
        commands.setLayoutX(60);
        commands.setLayoutY(420);
        Group group = new Group(circle, commands);
        Scene scene = new Scene(group, 400, 500);
        startButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.STOPPED));
        pauseButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.RUNNING));

67

Chapter 2 ■ Creating a User Interface in JavaFX

        resumeButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.PAUSED));
        stopButton.disableProperty().bind(anim.statusProperty()
                .isEqualTo(Animation.Status.STOPPED));
        stage.setScene(scene);
        stage.setTitle("Metronome using TranslateTransition");
        stage.show();
    }
}

Using the TranslateTransition Class
As shown in the following snippet from Listing 2-6, to create a TranslateTransition we’re supplying
values that are reminiscent of the values that we used when creating a timeline in the previous example. For
example, we’re setting autoReverse to true and cycleCount to Animation.INDEFINITE. Also, just as when
creating a KeyFrame for a timeline, we’re supplying a duration and an interpolation type here as well.
In addition, we’re supplying some values to properties that are specific to a TranslateTransition,
namely fromX and toX. These values are interpolated over the requested duration and assigned to the
layoutX property of the node controlled by the transition (in this case, the circle). If we also wanted to cause
vertical movement, assigning values to fromY and toY would cause interpolated values between them to be
assigned to the layoutY property.
An alternative to supplying toX and toY values is to provide values to the byX and byY properties, which
enables you to specify the distance to travel in each direction rather than start and end points. Also, if you
don’t supply a value for fromX, the interpolation will begin with the current value of the node’s layoutX
property. The same holds true for fromY (if not supplied, the interpolation will begin with the value of
layoutY).
circle = new Circle(100, 50, 4, Color.BLUE);
TranslateTransition anim = new TranslateTransition(new Duration(1000.0), circle);
anim.setFromX(0);
anim.setToX(200);
anim.setAutoReverse(true);
anim.setCycleCount(Animation.INDEFINITE);
anim.setInterpolator(Interpolator.LINEAR);

Controlling and Monitoring the Transition
The TranslateTransition class, as do all of the classes in Table 2-1, extends the javafx.animation.
Transition class, which in turn extends the Animation class. Because the Timeline class extends the
Animation class, as you can see by comparing Listings 2-5 and 2-6, all of the code for the buttons in
this example is identical to that in the previous example. Indeed, the functionality required to start,
pause, resume, and stop an animation is defined on the Animation class itself, and inherited by both the
Translation classes as well as the Timeline class.

68

Chapter 2 ■ Creating a User Interface in JavaFX

The MetronomePathTransition Example
As shown in Table 2-1, PathTransition is a transition class that enables you to move a node along a
defined geometric path. Figure 2-10 shows a screenshot of a version of the metronome example, named
MetronomePathTransition, that demonstrates how to use the PathTransition class.

Figure 2-10. The MetronomePathTransition program

The Behavior of the MetronomePathTransition Program
Go ahead and run the program, performing once again the same steps that you did for the Metronome1
exercise. Everything should function the same as it did in the MetronomeTransition example, except that the
node is an ellipse instead of a circle, and the node moves along the path of an arc.

Understanding the MetronomePathTransition Program
Listing 2-7 contains code snippets from the MetronomePathTransition program that highlight the
differences from the preceding (MetronomeTransition) program. Take a look at the code, and then we
review relevant concepts.

69

Chapter 2 ■ Creating a User Interface in JavaFX

Listing 2-7. Portions of MetronomePathTransitionMain.java
package projavafx.metronomepathtransition.ui;
...imports omitted...
public class MetronomePathTransitionMain extends Application {
    Button startButton;
    Button pauseButton;
    Button resumeButton;
    Button stopButton;
    Ellipse ellipse;
    Path path;
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        ellipse = new Ellipse(100, 50, 4, 8);
        ellipse.setFill(Color.BLUE);
        path = new Path(
                new MoveTo(100, 50),
                new ArcTo(350, 350, 0, 300, 50, false, true)
        );
        PathTransition anim = new PathTransition(new Duration(1000.0), path, ellipse);
        anim.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
        anim.setInterpolator(Interpolator.LINEAR);
        anim.setAutoReverse(true);
        anim.setCycleCount(Timeline.INDEFINITE);
        startButton = new Button("start");
        startButton.setOnAction(e -> anim.playFromStart());
        pauseButton = new Button("pause");
        pauseButton.setOnAction(e -> anim.pause());
        resumeButton = new Button("resume");
        resumeButton.setOnAction(e -> anim.play());
        stopButton = new Button("stop");
        stopButton.setOnAction(e -> anim.stop());
        HBox commands = new HBox(10, startButton,
                pauseButton,
                resumeButton,
                stopButton);
        commands.setLayoutX(60);
        commands.setLayoutY(420);
        Group group = new Group(ellipse, commands);
        Scene scene = new Scene(group, 400, 500);
        startButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.STOPPED));

70

Chapter 2 ■ Creating a User Interface in JavaFX

        pauseButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.RUNNING));
        resumeButton.disableProperty().bind(anim.statusProperty()
                .isNotEqualTo(Animation.Status.PAUSED));
        stopButton.disableProperty().bind(anim.statusProperty()
                .isEqualTo(Animation.Status.STOPPED));
        stage.setScene(scene);
        stage.setTitle("Metronome using PathTransition");
        stage.show();
    }
}

Using the PathTransition Class
As shown in Listing 2-7, defining a PathTransition includes supplying an instance of type Path to the path
property that represents the geometric path that the node is to travel. Here we’re creating a Path instance
that defines an arc beginning at 100 pixels on the x axis and 50 pixels on the y axis, ending at 300 pixels on
the x axis and 50 pixels on the y axis, with 350 pixel horizontal and vertical radii. This is accomplished by
creating a Path that contains the MoveTo and ArcTo path elements. Take a look at the javafx.scene.shape
package in the JavaFX API docs for more information on the PathElement class and its subclasses, which are
used for creating a path.

■■Tip The properties in the ArcTo class are fairly intuitive except for sweepFlag. If sweepFlag is true, the line
joining the center of the arc to the arc itself sweeps through increasing angles; otherwise, it sweeps through
decreasing angles.
Another property of the PathTransition class is orientation, which controls whether the node’s
orientation remains unchanged or stays perpendicular to the path’s tangent as it moves along the path.
Listing 2-7 uses the OrientationType.ORTHOGONAL_TO_TANGENT constant to accomplish the latter, as the
former is the default.

Drawing an Ellipse
As shown in Listing 2-7, drawing an Ellipse is similar to drawing a Circle, the difference being that an
additional radius is required (radiusX and radiusY instead of just radius).
Now that you've learned how to animate nodes by creating a timeline and by creating transitions, we
create a very simple Pong-style game that requires animating a ping-pong ball. In the process, you learn how
to detect when the ball has hit a paddle or wall in the game.

The Zen of Node Collision Detection
When animating a node, you sometimes need to know when the node has collided with another node. To
demonstrate this capability, our colleague Chris Wright developed a simple version of the Pong-style game
that we call ZenPong. Originally, we asked him to build the game with only one paddle, which brought the
famous Zen koan (philosophical riddle) “What is the sound of one hand clapping?” to mind. Chris had

71

Chapter 2 ■ Creating a User Interface in JavaFX

so much fun developing the game that he snuck a second paddle in, but we’re still calling this example
ZenPong. Figure 2-11 shows this very simple form of the game when first invoked.

Figure 2-11. The initial state of the ZenPong game
Try out the game by following the instructions in the upcoming exercise, remembering that you control
both paddles (unless you can get a colleague to share your keyboard and play).

EXAMINING THE BEHAVIOR OF THE ZENPONG GAME
When the program starts, its appearance should be similar to the screenshot in Figure 2-11. To fully
examine its behavior, perform the following steps.

72

1.

Before clicking Start, drag each of the paddles vertically to other positions. One
game cheat is to drag the left paddle up and the right paddle down, which will put
them in good positions to respond to the ball after being served.

2.

Practice using the A key to move the left paddle up, the Z key to move the left
paddle down, the L key to move the right paddle up, and the comma (,) key to move
the right paddle down.

3.

Click Start to begin playing the game. Notice that the Start button disappears and
the ball begins moving at a 45° angle, bouncing off paddles and the top and bottom
walls. The screen should look similar to Figure 2-12.

Chapter 2 ■ Creating a User Interface in JavaFX

Figure 2-12. The ZenPong game in action

4.

If the ball hits the left or right wall, one of your hands has lost the game. Notice that
the game resets, looking again like the screenshot in Figure 2-11.

Now that you’ve experienced the behavior of the ZenPong program, let’s review the code behind it.

Understanding the ZenPong Program
Examine the code for the ZenPong program in Listing 2-8, before we highlight some concepts demonstrated
within.
Listing 2-8. ZenPongMain.java
package projavafx.zenpong.ui;
...imports omitted...
public class ZenPongMain extends Application {
    /**
     * The center points of the moving ball
     */

73

Chapter 2 ■ Creating a User Interface in JavaFX

    DoubleProperty centerX = new SimpleDoubleProperty();
    DoubleProperty centerY = new SimpleDoubleProperty();
    /**
     * The Y coordinate of the left paddle
     */
    DoubleProperty leftPaddleY = new SimpleDoubleProperty();
    /**
     * The Y coordinate of the right paddle
     */
    DoubleProperty rightPaddleY = new SimpleDoubleProperty();
    /**
     * The drag anchor for left and right paddles
     */
    double leftPaddleDragAnchorY;
    double rightPaddleDragAnchorY;
    /**
     * The initial translateY property for the left and right paddles
     */
    double initLeftPaddleTranslateY;
    double initRightPaddleTranslateY;
    /**
     * The moving ball
     */
    Circle ball;
    /**
     * The Group containing all of the walls, paddles, and ball. This also
     * allows us to requestFocus for KeyEvents on the Group
     */
    Group pongComponents;
    /**
     * The left and right paddles
     */
    Rectangle leftPaddle;
    Rectangle rightPaddle;
    /**
     * The walls
     */
    Rectangle topWall;
    Rectangle rightWall;
    Rectangle leftWall;
    Rectangle bottomWall;
    Button startButton;

74

Chapter 2 ■ Creating a User Interface in JavaFX

    /**
     * Controls whether the startButton is visible
     */
    BooleanProperty startVisible = new SimpleBooleanProperty(true);
    /**
     * The animation of the ball
     */
    Timeline pongAnimation;
    /**
     * Controls whether the ball is moving right
     */
    boolean movingRight = true;
    /**
     * Controls whether the ball is moving down
     */
    boolean movingDown = true;
    /**
     * Sets the initial starting positions of the ball and paddles
     */
    void initialize() {
        centerX.setValue(250);
        centerY.setValue(250);
        leftPaddleY.setValue(235);
        rightPaddleY.setValue(235);
        startVisible.set(true);
        pongComponents.requestFocus();
    }
    /**
     * Checks whether or not the ball has collided with either the paddles,
     * topWall, or bottomWall. If the ball hits the wall behind the paddles, the
     * game is over.
     */
    void checkForCollision() {
        if (ball.intersects(rightWall.getBoundsInLocal())
                || ball.intersects(leftWall.getBoundsInLocal())) {
            pongAnimation.stop();
            initialize();
        } else if (ball.intersects(bottomWall.getBoundsInLocal())
                || ball.intersects(topWall.getBoundsInLocal())) {
            movingDown = !movingDown;
        } else if (ball.intersects(leftPaddle.getBoundsInParent()) && !movingRight) {
            movingRight = !movingRight;
        } else if (ball.intersects(rightPaddle.getBoundsInParent()) && movingRight) {
            movingRight = !movingRight;
        }
    }

75

Chapter 2 ■ Creating a User Interface in JavaFX

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Application.launch(args);
    }
    @Override
    public void start(Stage stage) {
        pongAnimation = new Timeline(
                new KeyFrame(new Duration(10.0), t -> {
                    checkForCollision();
                    int horzPixels = movingRight ? 1 : -1;
                    int vertPixels = movingDown ? 1 : -1;
                    centerX.setValue(centerX.getValue() + horzPixels);
                    centerY.setValue(centerY.getValue() + vertPixels);
                })
        );
        pongAnimation.setCycleCount(Timeline.INDEFINITE);
        ball = new Circle(0, 0, 5, Color.WHITE);
        topWall = new Rectangle(0, 0, 500, 1);
        leftWall = new Rectangle(0, 0, 1, 500);
        rightWall = new Rectangle(500, 0, 1, 500);
        bottomWall = new Rectangle(0, 500, 500, 1);
        leftPaddle = new Rectangle(20, 0, 10, 30);
        leftPaddle.setFill(Color.LIGHTBLUE);
        leftPaddle.setCursor(Cursor.HAND);
        leftPaddle.setOnMousePressed(me -> {
            initLeftPaddleTranslateY = leftPaddle.getTranslateY();
            leftPaddleDragAnchorY = me.getSceneY();
        });
        leftPaddle.setOnMouseDragged(me -> {
            double dragY = me.getSceneY() - leftPaddleDragAnchorY;
            leftPaddleY.setValue(initLeftPaddleTranslateY + dragY);
        });
        rightPaddle = new Rectangle(470, 0, 10, 30);
        rightPaddle.setFill(Color.LIGHTBLUE);
        rightPaddle.setCursor(Cursor.CLOSED_HAND);
        rightPaddle.setOnMousePressed(me -> {
            initRightPaddleTranslateY = rightPaddle.getTranslateY();
            rightPaddleDragAnchorY = me.getSceneY();
        });
        rightPaddle.setOnMouseDragged(me -> {
            double dragY = me.getSceneY() - rightPaddleDragAnchorY;
            rightPaddleY.setValue(initRightPaddleTranslateY + dragY);
        });
        startButton = new Button("Start!");
        startButton.setLayoutX(225);
        startButton.setLayoutY(470);
        startButton.setOnAction(e -> {
            startVisible.set(false);

76

Chapter 2 ■ Creating a User Interface in JavaFX

            pongAnimation.playFromStart();
            pongComponents.requestFocus();
        });
        pongComponents = new Group(ball,
                topWall,
                leftWall,
                rightWall,
                bottomWall,
                leftPaddle,
                rightPaddle,
                startButton);
        pongComponents.setFocusTraversable(true);
        pongComponents.setOnKeyPressed(k -> {
            if (k.getCode() == KeyCode.SPACE
                    && pongAnimation.statusProperty()
                    .equals(Animation.Status.STOPPED)) {
                rightPaddleY.setValue(rightPaddleY.getValue() - 6);
            } else if (k.getCode() == KeyCode.L
                    && !rightPaddle.getBoundsInParent().intersects(topWall.
getBoundsInLocal())) {
                rightPaddleY.setValue(rightPaddleY.getValue() - 6);
            } else if (k.getCode() == KeyCode.COMMA
                    && !rightPaddle.getBoundsInParent().intersects(bottomWall.
getBoundsInLocal())) {
                rightPaddleY.setValue(rightPaddleY.getValue() + 6);
            } else if (k.getCode() == KeyCode.A
                    && !leftPaddle.getBoundsInParent().intersects(topWall.
getBoundsInLocal())) {
                leftPaddleY.setValue(leftPaddleY.getValue() - 6);
            } else if (k.getCode() == KeyCode.Z
                    && !leftPaddle.getBoundsInParent().intersects(bottomWall.
getBoundsInLocal())) {
                leftPaddleY.setValue(leftPaddleY.getValue() + 6);
            }
        });
        Scene scene = new Scene(pongComponents, 500, 500);
        scene.setFill(Color.GRAY);
        ball.centerXProperty().bind(centerX);
        ball.centerYProperty().bind(centerY);
        leftPaddle.translateYProperty().bind(leftPaddleY);
        rightPaddle.translateYProperty().bind(rightPaddleY);
        startButton.visibleProperty().bind(startVisible);
        stage.setScene(scene);
        initialize();
        stage.setTitle("ZenPong Example");
        stage.show();
    }
}

77

Chapter 2 ■ Creating a User Interface in JavaFX

Using the KeyFrame Action Event Handler
We’re using a different technique in the timeline than demonstrated in the Metronome1 program earlier in
the chapter (see Figure 2-8 and Listing 2-5). Instead of interpolating two values over a period of time, we’re
using the action event handler of the KeyFrame instance in our timeline. Take a look at the following snippet
from Listing 2-8 to see this technique in use.
pongAnimation = new Timeline(
                new KeyFrame(new Duration(10.0), t -> {
                    checkForCollision();
                    int horzPixels = movingRight ? 1 : -1;
                    int vertPixels = movingDown ? 1 : -1;
                    centerX.setValue(centerX.getValue() + horzPixels);
                    centerY.setValue(centerY.getValue() + vertPixels);
                })
);
pongAnimation.setCycleCount(Timeline.INDEFINITE);
As shown in the snippet, we use only one KeyFrame, and it has a very short time (10 milliseconds).
When a KeyFrame has an action event handler, the code in that handler—which in this case is once again a
lambda expression—is executed when the time for that KeyFrame is reached. Because the cycleCount of this
timeline is indefinite, the action event handler will be executed every 10 milliseconds. The code in this event
handler does two things:
•

Calls a method named checkForCollision(), which is defined in this program, the
purpose of which is to see whether the ball has collided with either paddle or any of
the walls

•

Updates the properties in the model to which the position of the ball is bound, taking
into account the direction in which the ball is already moving

Using the Node intersects( ) Method to Detect Collisions
Take a look inside the checkForCollision() method in the following snippet from Listing 2-8 to see how we
check for collisions by detecting when two nodes intersect (share any of the same pixels).
void checkForCollision() {
  if (ball.intersects(rightWall.getBoundsInLocal()) ||
      ball.intersects(leftWall.getBoundsInLocal())) {
    pongAnimation.stop();
    initialize();
  }
  else if (ball.intersects(bottomWall.getBoundsInLocal()) ||
           ball.intersects(topWall.getBoundsInLocal())) {
    movingDown = !movingDown;
  }
  else if (ball.intersects(leftPaddle.getBoundsInParent()) && !movingRight) {
    movingRight = !movingRight;
  }
  else if (ball.intersects(rightPaddle.getBoundsInParent()) && movingRight) {
    movingRight = !movingRight;
  }
}

78

Chapter 2 ■ Creating a User Interface in JavaFX

The intersects() method of the Node class shown here takes an argument of type Bounds, located in
the javafx.geometry package. It represents the rectangular bounds of a node, for example, the leftPaddle
node shown in the preceding code snippet. Notice that to get the position of the left paddle in the Group that
contains it, we’re using the boundsInParent property that the leftPaddle (a Rectangle) inherited from the
Node class.
The net results of the intersect method invocations in the preceding snippet are as follows.
•

If the ball intersects with the bounds of the rightWall or leftWall, the
pongAnimation Timeline is stopped and the game is initialized for the next play.
Note that the rightWall and left Wall nodes are one-pixel-wide rectangles on the
left and right sides of the Scene. Take a peek at Listing 2-8 to see where these are
defined.

•

If the ball intersects with the bounds of the bottomWall or topWall, the vertical
direction of the ball will be changed by negating the program’s Boolean movingDown
variable.

•

If the ball intersects with the bounds of the leftPaddle or rightPaddle, the
horizontal direction of the ball will be changed by negating the program’s Boolean
movingRight variable.

■■Tip

For more information on boundsInParent and its related properties, layoutBounds and
boundsInLocal, see the “Bounding Rectangles” discussion at the beginning of the javafx.scene.Node class in
the JavaFX API docs. For example, it is a common practice to find out the width or height of a node by using the
expression myNode.getLayoutBounds().getWidth() or myNode.getLayoutBounds().getHeight().

Dragging a Node
As you experienced previously, the paddles of the ZenPong application may be dragged with the mouse.
The following snippet from Listing 2-8 shows how this capability is implemented in ZenPong for dragging
the right paddle.
  DoubleProperty rightPaddleY = new SimpleDoubleProperty();
  ...code omitted...
  double rightPaddleDragStartY;
  double rightPaddleDragAnchorY;
  ...code omitted...
  void initialize() {
...code omitted...
    rightPaddleY.setValue(235);
  }
  ...code omitted...
rightPaddle = new Rectangle(470, 0, 10, 30);
rightPaddle.setFill(Color.LIGHTBLUE);
rightPaddle.setCursor(Cursor.CLOSED_HAND);
rightPaddle.setOnMousePressed(me -> {
    initRightPaddleTranslateY = rightPaddle.getTranslateY();
    rightPaddleDragAnchorY = me.getSceneY();
});

79

Chapter 2 ■ Creating a User Interface in JavaFX

rightPaddle.setOnMouseDragged(me -> {
    double dragY = me.getSceneY() - rightPaddleDragAnchorY;
    rightPaddleY.setValue(initRightPaddleTranslateY + dragY);
});
...code omitted...
rightPaddle.translateYProperty().bind(rightPaddleY);
Note that in this ZenPong example, we’re dragging the paddles only vertically, not horizontally
Therefore, the code snippet only deals with dragging on the y axis. After creating the paddle at the initial
location, we register event handlers for MousePressed and MouseDragged events. The latter manipulates the
rightPaddleY property, which is used for translating the paddle along the y axis. Properties and bindings
will be explained in detail in Chapter 3.

Giving Keyboard Input Focus to a Node
For a node to receive key events, it has to have keyboard focus. This is accomplished in the ZenPong example
by doing these two things, as shown in the snippet that follows from Listing 2-8:
•

Assigning true to the focusTraversable property of the Group node. This allows the
node to accept keyboard focus.

•

Calling the requestFocus() method of the Group node (referred to by the
pongComponents variable). This requests that the node obtain focus.

■■Tip You cannot directly set the value of the focused property of a Stage. Consulting the API docs also
reveals that you cannot set the value of the focused property of a Node (e.g., the Group that we’re discussing
now). However, as discussed in the second point just mentioned, you can call requestFocus() on the node,
which if granted (and focusTraversable is true) sets the focused property to true. By the way, Stage doesn’t
have a requestFocus() method, but it does have a toFront() method, which should give it keyboard focus.
...code omitted...
pongComponents.setFocusTraversable(true);
pongComponents.setOnKeyPressed(k -> {
    if (k.getCode() == KeyCode.SPACE
            && pongAnimation.statusProperty()
            .equals(Animation.Status.STOPPED)) {
        rightPaddleY.setValue(rightPaddleY.getValue() - 6);
    } else if (k.getCode() == KeyCode.L
            && !rightPaddle.getBoundsInParent().intersects(topWall.getBoundsInLocal())) {
        rightPaddleY.setValue(rightPaddleY.getValue() - 6);
    } else if (k.getCode() == KeyCode.COMMA
            && !rightPaddle.getBoundsInParent().intersects(bottomWall.getBoundsInLocal())) {
        rightPaddleY.setValue(rightPaddleY.getValue() + 6);
    } else if (k.getCode() == KeyCode.A
            && !leftPaddle.getBoundsInParent().intersects(topWall.getBoundsInLocal())) {
        leftPaddleY.setValue(leftPaddleY.getValue() - 6);
    } else if (k.getCode() == KeyCode.Z

80

Chapter 2 ■ Creating a User Interface in JavaFX

            && !leftPaddle.getBoundsInParent().intersects(bottomWall.getBoundsInLocal())) {
        leftPaddleY.setValue(leftPaddleY.getValue() + 6);
    }
});
Now that the node has focus, when the user interacts with the keyboard, the appropriate event handlers
will be invoked. In this example, we’re interested in whenever certain keys are pressed, as discussed next.

Using the onKeyPressed Event Handler
When the user presses a key, the lambda expression supplied to the onKeyPressed method is invoked,
passing a KeyEvent instance that contains information about the event. The method body of this expression,
shown in the preceding snippet from Listing 2-8, compares the getCode() method of the KeyEvent instance
to the KeyCode constants that represent the arrow keys to ascertain which key was pressed.

Summary
Congratulations! You have learned a lot in this chapter about creating UIs in JavaFX, including the following.
•

Creating a UI in JavaFX, which we loosely based on the metaphor of creating a
theater play, and typically consists of creating a stage, a scene, nodes, a model, and
event handlers, and animating some of the nodes

•

The details about using most of the properties and methods of the Stage class,
including how to create a Stage that is transparent with no window decorations

•

How to use the HBox and VBox layout containers to organize nodes horizontally and
vertically, respectively

•

The details about using many of the properties and methods of the Scene class

•

How to create and apply CSS styles to nodes in your program by associating one or
more style sheets with the Scene

•

How to handle keyboard and mouse input events

•

How to animate nodes in the scene, both with the Timeline class and the transition
classes

•

How to detect when nodes in the scene have collided

In Chapter 3, we will discuss the alternative approach for creating UIs, this time with Scene Builder.
Then, in Chapter 4, we take a deeper dive into the areas of properties and binding.

Resources
For some additional information on creating JavaFX UIs, you can consult the following resources.
•

JavaFX 9 SDK documentation online: http://download.java.net/jdk9/jfxdocs/

•

JavaFX 9 CSS Reference Guide: http://docs.oracle.com/javase/9/docs/api/
javafx/scene/doc-files/cssref.html

•

The w3schools.com CSS Tutorial: www.w3schools.com/css

81

CHAPTER 3

Properties and Bindings
Heaven acts with vitality and persistence.
In correspondence with this
The superior person keeps himself vital without ceasing.
—I Ching
In Chapters 1 and 2, we introduced you to the JavaFX 9 platform that is part of Oracle JDK 9. You set up
your development environment with your favorite IDE: Eclipse, NetBeans, or IntelliJ IDEA. You wrote and
ran your first JavaFX GUI programs. You learned the fundamental building blocks of JavaFX: the Stage and
Scene classes, and the Nodes that go into the Scene. You have no doubt noticed the use of user-defined model
classes to represent the application state and have that state communicated to the UI through properties and
bindings.
In this chapter, we give you a guided tour of the JavaFX properties and bindings framework. After
recalling a little bit of history and presenting a motivating example that shows various ways that a
JavaFX Property can be used, we cover key concepts of the framework: Observable, ObservableValue,
WritableValue, ReadOnlyProperty, Property, and Binding. We show you the capabilities offered by these
fundamental interfaces of the framework. We then show you how Property objects are bound together, how
Binding objects are built out of properties and other bindings—using the factory methods in the Bindings
utility class, the fluent interface API, or going low level by directly extending abstract classes that implement
the Binding interface—and how they are used to easily propagate changes in one part of a program to other
parts of the program without too much coding. We then introduce the JavaFX Beans naming convention, an
extension of the original JavaBeans naming convention that makes organizing your data into encapsulated
components an orderly affair. We finish this chapter by showing how to adapt old-style JavaBeans properties
into JavaFX properties.
Because the JavaFX properties and bindings framework is a nonvisual part of the JavaFX platform, the
example programs in this chapter are also nonvisual in nature. We deal with Boolean, Integer, Long, Float,
Double, String, and Object type properties and bindings as these are the types in which the JavaFX binding
framework specializes. Your GUI building fun resumes in the next and further chapters.

Forerunners of JavaFX Binding
The need for exposing attributes of Java components directly to client code, allowing them to observe and to
manipulate such attributes, and to take action when their values change, was recognized early in Java’s life.
The JavaBeans framework in Java 1.1 provided support for properties through the now familiar getter and
setter convention. It also supported the propagations of property changes through its PropertyChangeEvent
and PropertyChangeListener mechanism. Although the JavaBeans framework is used in many Swing

© Johan Vos, Stephen Chin, Weiqi Gao, James Weaver, and Dean Iverson 2018
J. Vos et al., Pro JavaFX 9, https://doi.org/10.1007/978-1-4842-3042-8_3

83

Chapter 3 ■ Properties and Bindings

applications, its use is quite cumbersome and requires quite a bit of boilerplate code. Several higher-level
data binding frameworks were created over the years with various levels of success. The heritage of the
JavaBeans in the JavaFX properties and bindings framework lies mainly in the JavaFX Beans getter, setter,
and property getter naming convention when defining JavaFX components. We talk about the JavaFX Beans
getter, setter, and property getter naming convention later in this chapter, after we have covered the key
concepts and interfaces of the JavaFX properties and bindings framework.
Another strand of heritage of the JavaFX properties and bindings framework comes from the
JavaFX Script language that was part of the JavaFX 1.x platform. Although the JavaFX Script language
was deprecated in the JavaFX platform in favor of a Java-based API, one of the goals of the transition
was to preserve most of the powers of the JavaFX Script’s bind keyword, the expressive power of which
has delighted many JavaFX enthusiasts. As an example, JavaFX Script supports the binding to complex
expressions:
var
var
var
def

a
b
m
c

=
=
=
=

1;
10;
4;
bind for (x in [a..b] where x < m) { x * x };

This code will automatically recalculate the value of c whenever the values of a, b, or m are changed.
Although the JavaFX properties and bindings framework does not support all of the binding constructs
of JavaFX Script, it supports the binding of many useful expressions. We talk more about constructing
compound binding expressions after we cover the key concepts and interfaces of the framework.

A Motivating Example
Let’s start with an example in Listing 3-1 that shows off the capabilities of the Property interface through the
use of a couple of instances of the SimpleIntegerProperty class.
Listing 3-1. MotivatingExample.java
import
import
import
import
import

javafx.beans.InvalidationListener;
javafx.beans.property.IntegerProperty;
javafx.beans.property.SimpleIntegerProperty;
javafx.beans.value.ChangeListener;
javafx.beans.value.ObservableValue;

public class MotivatingExample {
    private static IntegerProperty intProperty;
    public static void main(String[] args) {
        createProperty();
        addAndRemoveInvalidationListener();
        addAndRemoveChangeListener();
        bindAndUnbindOnePropertyToAnother();
    }
    private static void createProperty() {
        System.out.println();
        intProperty = new SimpleIntegerProperty(1024);
        System.out.println("intProperty = " + intProperty);
        System.out.println("intProperty.get() = " + intProperty.get());

84

Chapter 3 ■ Properties and Bindings

        System.out.println("intProperty.getValue() = " + intProperty.getValue().intValue());
    }
    private static void addAndRemoveInvalidationListener() {
        System.out.println();
        final InvalidationListener invalidationListener = observable ->
            System.out.println("The observable has been invalidated: " + observable + ".");
        intProperty.addListener(invalidationListener);
        System.out.println("Added invalidation listener.");
        System.out.println("Calling intProperty.set(2048).");
        intProperty.set(2048);
        System.out.println("Calling intProperty.setValue(3072).");
        intProperty.setValue(Integer.valueOf(3072));
        intProperty.removeListener(invalidationListener);
        System.out.println("Removed invalidation listener.");
        System.out.println("Calling intProperty.set(4096).");
        intProperty.set(4096);
    }
    private static void addAndRemoveChangeListener() {
        System.out.println();
        final ChangeListener changeListener = (ObservableValue observableValue, Object
oldValue, Object newValue) ->
            
System.out.println("The observableValue has changed: oldValue = " + oldValue + ",
newValue = " + newValue);
        intProperty.addListener(changeListener);
        System.out.println("Added change listener.");
        System.out.println("Calling intProperty.set(5120).");
        intProperty.set(5120);
        intProperty.removeListener(changeListener);
        System.out.println("Removed change listener.");
        System.out.println("Calling intProperty.set(6144).");
        intProperty.set(6144);
    }
    private static void bindAndUnbindOnePropertyToAnother() {
        System.out.println();
        IntegerProperty otherProperty = new SimpleIntegerProperty(0);
        System.out.println("otherProperty.get() = " + otherProperty.get());
        System.out.println("Binding otherProperty to intProperty.");
        otherProperty.bind(intProperty);
        System.out.println("otherProperty.get() = " + otherProperty.get());

85

Chapter 3 ■ Properties and Bindings

        System.out.println("Calling intProperty.set(7168).");
        intProperty.set(7168);
        System.out.println("otherProperty.get() = " + otherProperty.get());
        System.out.println("Unbinding otherProperty from intProperty.");
        otherProperty.unbind();
        System.out.println("otherProperty.get() = " + otherProperty.get());
        System.out.println("Calling intProperty.set(8192).");
        intProperty.set(8192);
        System.out.println("otherProperty.get() = " + otherProperty.get());
    }
}
In this example we created a SimpleIntegerProperty object called intProperty with an initial value
of 1024. We then updated its value through a series of different integers while we added and then removed
an InvalidationListener, added and then removed a ChangeListener, and finally, created another
SimpleIntegerProperty named otherProperty, bound it to, and then unbound it from the intProperty.
We have taken advantage of the Java 8 lambda syntax in defining our listeners. The sample program used a
generous amount of println calls to show what is happening inside the program.
When we run the program in Listing 3-1, the following output is printed to the console:
intProperty = IntegerProperty [value: 1024]
intProperty.get() = 1024
intProperty.getValue() = 1024
Added invalidation listener.
Calling intProperty.set(2048).
The observable has been invalidated: IntegerProperty [value: 2048].
Calling intProperty.setValue(3072).
The observable has been invalidated: IntegerProperty [value: 3072].
Removed invalidation listener.
Calling intProperty.set(4096).
Added change listener.
Calling intProperty.set(5120).
The observableValue has changed: oldValue = 4096, newValue = 5120
Removed change listener.
Calling intProperty.set(6144).
otherProperty.get() = 0
Binding otherProperty to intProperty.
otherProperty.get() = 6144
Calling intProperty.set(7168).
otherProperty.get() = 7168
Unbinding otherProperty from intProperty.
otherProperty.get() = 7168
Calling intProperty.set(8192).
otherProperty.get() = 7168

86

Chapter 3 ■ Properties and Bindings

By correlating the output lines with the program source code (or by stepping through the code in the
debugger of your favorite IDE), we can draw the following conclusions.
•

A SimpleIntegerProperty object such as intProperty and otherProperty holds
an int value. The value can be manipulated with the get(), set(), getValue(), and
setValue() methods. The get() and set() methods perform their operation with
the primitive int type. The getValue() and setValue() methods use the Integer
wrapper type.

•

You can add and remove InvalidationListener objects to and from intProperty.

•

You can add and remove ChangeListener objects to and from intProperty.

•

Another Property object such as otherProperty can bind itself to intProperty.
When that happens, otherProperty receives the value of intProperty.

•

When a new value is set on intProperty, whatever object that is attached to it is
notified. The notification is not sent if the object is removed.

•

When notified, InvalidationListener objects are only informed of which object is
sending out the notification and that object is only known as an Observable.

•

When notified, ChangeListener objects are informed on two more pieces of
information—the oldValue and the newValue—in addition to the object sending the
notification. The sending object is known as an ObservableValue.

•

In the case of a binding property such as otherProperty, we cannot tell from the
output when or how it is notified of the change of value in intProperty. However,
we can infer that it must have known of the change because when we asked
otherProperty for its value we got back the latest value of intProperty.

■■Note Even though this motivating example uses an Integer property, similar examples can be made to
use properties based on the Boolean, Long, Float, Double, String, and Object types. In the JavaFX properties
and bindings framework, when interfaces are extended or implemented for concrete types, they are always
done for the Boolean, Integer, Long, Float, Double, String, and Object types.
This example brings to our attention some of the key interfaces and concepts of the JavaFX properties
and bindings framework: including the Observable and the associated InvalidationListener interfaces,
the ObservableValue and the associated ChangeListener interfaces, the get(), set(), getValue(), and
setValue() methods that allow us to manipulate the values of a SimpleIntegerProperty object directly, and
the bind() method that allows us to relinquish direct manipulation of the value of a SimpleIntegerProperty
object by subordinating it to another SimpleIntegerProperty object.
In the next section, we show you these and some other key interfaces and concepts of the JavaFX
properties and bindings framework in more detail.

Understanding Key Interfaces and Concepts
Figure 3-1 is a UML diagram showing the key interfaces of the JavaFX properties and bindings framework. It
includes some interfaces that you saw in the last section, and some that you haven’t yet seen.

87

Chapter 3 ■ Properties and Bindings

Figure 3-1. Key interfaces of the JavaFX properties and bindings framework

■■Note We did not show you the fully qualified names of the interfaces in the UML diagram. These interfaces
are spread out in four packages: javafx.beans, javafx.beans.binding, javafx.beans.property, and
javafx.beans.value. You can easily figure out which interface belongs to which package by examining the
JavaFX API documentation or by the “find class” feature of your favorite IDE.

Understanding the Observable Interface
At the root of the hierarchy is the Observable interface. You can register InvalidationListener objects to
an Observable object to receive invalidation events. You have already seen invalidation events fired from
one kind of Observable object, the SimpleIntegerProperty object intProperty in the motivating example
in the last section. It is fired when the set() or setValue() methods are called to change the underlying
value from one int to a different int.

■■Note An invalidation event is fired only once by any of the implementations of the Property interface in
the JavaFX properties and bindings framework if you call the setter with the same value several times in a row.

88

Chapter 3 ■ Properties and Bindings

Another place where invalidation events are fired is from Binding objects. You haven’t seen an example
of a Binding object yet, but there are plenty of Binding objects in the second half of this chapter. For now we
just note that a Binding object may become invalid, for example, when its invalidate() method is called, or
as we show later in this chapter, when one of its dependencies fires an invalidation event.

■■Note An invalidation event is fired only once by any of the implementations of the Binding interface in the
JavaFX properties and bindings framework if it becomes invalid several times in a row.

Understanding the ObservableValue Interface
Next up in the hierarchy is the ObservableValue interface. It’s simply an Observable that has a
value. Its getValue() method returns its value. The getValue() method that we called on the
SimpleIntegerProperty objects in the motivating example can be considered to have come from this
interface. You can register ChangeListener objects to an ObservableValue object to receive change events.
You saw change events being fired in the motivating example in the last section. When the change event
fires, the ChangeListener receives two more pieces of information: the old value and the new value of the
ObservableValue object.

■■Note A change event is fired only once by any of the implementations of the ObservableValue interface in
the JavaFX properties and bindings framework if you call the setter with the same value several times in a row.
The distinction between an invalidation event and a change event is made so that the JavaFX properties
and bindings framework may support lazy evaluations. We show an example of this by looking at three lines
of code from the motivating example:
otherProperty.bind(intProperty);
intProperty.set(7168);
System.out.println("otherProperty.get() = " + otherProperty.get());
When intProperty.set(7168) is called, it fires an invalidation event to otherProperty. On receiving
this invalidation event, otherProperty simply makes a note of the fact that its value is no longer valid. It does
not immediately perform a recalculation of its value by querying intProperty for its value. The recalculation
is performed later when otherProperty.get() is called. Imagine if instead of calling intProperty.set()
only once as in the preceding code we call intProperty.set() multiple times; otherProperty still
recalculates its value only once.

■■Note The ObservableValue interface is not the only direct subinterface of Observable. There are four
other direct subinterfaces of Observable that live in the javafx.collections package: ObservableList,
ObservableMap, ObservableSet, and ObservableArray with corresponding ListChangeListener,
MapChangeListener, SetChangeListener, and ArrayChangeListener as callback mechanisms. These JavaFX
observable collections are covered in Chapter 7.

89

Chapter 3 ■ Properties and Bindings

Understanding the WritableValue Interface
This might be the simplest subsection in the entire chapter, for the WritableValue interface is truly as simple
as it looks. Its purpose is to inject the getValue() and setValue() methods into implementations of this
interface. All implementation classes of WritableValue in the JavaFX properties and bindings framework
also implement ObservableValue; therefore, you can make an argument that the value of WritableValue is
only to provide the setValue() method.
You have seen the setValue() method at work in the motivating example.

Understanding the ReadOnlyProperty Interface
The ReadOnlyProperty interface injects two methods into its implementations. The getBean() method
should return the Object that contains the ReadOnlyRroperty or null if it is not contained in an Object.
The getName() method should return the name of the ReadOnlyProperty or the empty string if the
ReadOnlyProperty does not have a name.
The containing object and the name provide contextual information about a ReadOnlyProperty.
The contextual information of a property does not play any direct role in the propagation of invalidation
events or the recalculation of values. However, if provided, it will be taken into account in some peripheral
calculations.
In our motivating example, the intProperty is constructed without any contextual information. Had we
used the full constructor to supply it a name,
intProperty = new SimpleIntegerProperty(null, "intProperty", 1024);
the output would have contained the property name:
intProperty = IntegerProperty [name: intProperty, value: 1024]

Understanding the Property Interface
Now we come to the bottom of our key interfaces hierarchy. The Property interface has as its superinterfaces
all four interfaces we have examined thus far: Observable, ObservableValue, ReadOnlyProperty, and
WritableValue. Therefore, it inherits all the methods from these interfaces. It also provides five methods of
its own:
void bind(ObservableValue observableValue);
void unbind();
boolean isBound();
void bindBidirectional(Property tProperty);
void unbindBidirectional(Property tProperty);
You have seen two of the methods at work in the motivating example in the last section: bind() and
unbind().
Calling bind() creates a unidirectional binding or a dependency between the Property object and the
ObservableValue argument. Once they enter this relationship, calling the set() or setValue() methods on
the Property object will cause a RuntimeException to be thrown. Calling the get() or getValue() methods
on the Property object will return the value of the ObservableValue object. And, of course, changing the
value of the ObservableValue object will invalidate the Property object. Calling unbind() releases any
existing unidirectional binding the Property object may have. If a unidirectional binding is in effect, the
isBound() method returns true; otherwise, it returns false.

90

Chapter 3 ■ Properties and Bindings

Calling bindBidirectional() creates a bidirectional binding between the Property caller and the
Property argument. Notice that unlike the bind() method, which takes an ObservableValue argument,
the bindBidirectional() method takes a Property argument. Only two Property objects can be bound
together bidirectionally. Once they enter this relationship, calling the set() or setValue() methods on
either Property object will cause both objects’ values to be updated. Calling unbindBidirectional()
releases any existing bidirectional binding the caller and the argument may have. The program in Listing 3-2
shows a simple bidirectional binding at work.
Listing 3-2. BidirectionalBindingExample.java
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class BidirectionalBindingExample {
    public static void main(String[] args) {
        System.out.println("Constructing two StringProperty objects.");
        StringProperty prop1 = new SimpleStringProperty("");
        StringProperty prop2 = new SimpleStringProperty("");
        System.out.println("Calling bindBidirectional.");
        prop2.bindBidirectional(prop1);
        System.out.println("prop1.isBound() = " + prop1.isBound());
        System.out.println("prop2.isBound() = " + prop2.isBound());
        System.out.println("Calling prop1.set(\"prop1 says: Hi!\")");
        prop1.set("prop1 says: Hi!");
        System.out.println("prop2.get() returned:");
        System.out.println(prop2.get());
        System.out.println("Calling prop2.set(prop2.get() + \"\\nprop2 says: Bye!\")");
        prop2.set(prop2.get() + "\nprop2 says: Bye!");
        System.out.println("prop1.get() returned:");
        System.out.println(prop1.get());
    }
}
In this example we created two SimpleStringProperty objects called prop1 and prop2, created a
bidirectional binding between them, and then called set() and get() on both properties.
When we run the program in Listing 3-2, the following output is printed to the console:
Constructing two StringProperty objects.
Calling bindBidirectional.
prop1.isBound() = false
prop2.isBound() = false
Calling prop1.set("prop1 says: Hi!")
prop2.get() returned:
prop1 says: Hi!
Calling prop2.set(prop2.get() + "\nprop2 says: Bye!")
prop1.get() returned:
prop1 says: Hi!
prop2 says: Bye!

91

Chapter 3 ■ Properties and Bindings

■■Caution Each Property object may have at most one active unidirectional binding at a time. It may have
as many bidirectional bindings as you want. The isBound() method pertains only to unidirectional bindings.
Calling bind() a second time with a different ObservableValue argument while a unidirectional binding is
already in effect will unbind the existing one and replace it with the new one.

Understanding the Binding Interface
The Binding interface defines four methods that reveal the intentions of the interface. A Binding object is an
ObservableValue whose validity can be queried with the isValid() method and set with the invalidate()
method. It has a list of dependencies that can be obtained with the getDependencies() method. And finally
a dispose() method signals that the binding will not be used anymore and resources used by it can be
cleaned up.
From this brief description of the Binding interface, we can infer that it represents a unidirectional
binding with multiple dependencies. Each dependency, we imagine, could be an ObservableValue to which
the Binding is registered to receive invalidation events. When the get() or getValue() method is called, if
the binding is invalidated, its value is recalculated.
The JavaFX properties and bindings framework does not provide any concrete classes that implement
the Binding interface. However, it provides multiple ways to create your own Binding objects easily: You
can extend the abstract base classes in the framework; you can use a set of static methods in the utility class
Bindings to create new bindings out of existing regular Java values (i.e., unobservable values), properties,
and bindings; you can also use a set of methods that are provided in the various properties and bindings
classes and form a fluent interface API to create new bindings. We go through the utility methods and the
fluent interface API in the “Creating Bindings” section later in this chapter. For now, we show you the first
example of a binding by extending the DoubleBinding abstract class. The program in Listing 3-3 uses a
binding to calculate the area of a rectangle.
Listing 3-3. RectangleAreaExample.java
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class RectangleAreaExample {
    public static void main(String[] args) {
        System.out.println("Constructing x with initial value of 2.0.");
        final DoubleProperty x = new SimpleDoubleProperty(null, "x", 2.0);
        System.out.println("Constructing y with initial value of 3.0.");
        final DoubleProperty y = new SimpleDoubleProperty(null, "y", 3.0);
        System.out.println("Creating binding area with dependencies x and y.");
        DoubleBinding area = new DoubleBinding() {
            private double value;
            {
                super.bind(x, y);
            }
            @Override
            protected double computeValue() {
                System.out.println("computeValue() is called.");

92

Chapter 3 ■ Properties and Bindings

                return x.get() * y.get();
            }
        };
        System.out.println("area.get() = " + area.get());
        System.out.println("area.get() = " + area.get());
        System.out.println("Setting x to 5");
        x.set(5);
        System.out.println("Setting y to 7");
        y.set(7);
        System.out.println("area.get() = " + area.get());
    }
}
In the anonymous inner class, we called the protected bind() method in the superclass DoubleBinding,
informing the superclass that we would like to listen to invalidation events from the DoubleProperty
objects x and y. We finally implemented the protected abstract computeValue() method in the superclass
DoubleBinding to do the actual calculation when a recalculation is needed.
When we run the program in Listing 3-3, the following output is printed to the console:
Constructing x with initial value of 2.0.
Constructing y with initial value of 3.0.
Creating binding area with dependencies x and y.
computeValue() is called.
area.get() = 6.0
area.get() = 6.0
Setting x to 5
Setting y to 7
computeValue() is called.
area.get() = 35.0
Notice that computeValue() is called only once when we call area.get() twice in a row.

■■Caution The DoubleBinding abstract class contains a default implementation of dispose() that is empty
and a default implementation of getDependencies() that returns an empty list. To make this example a correct
Binding implementation, we should override these two methods to behave correctly.
Now that you have a firm grasp of the key interfaces and concepts of the JavaFX properties and bindings
framework, we show you how these generic interfaces are specialized to type-specific interfaces and
implemented in type-specific abstract and concrete classes.

Type-Specific Specializations of Key Interfaces
We did not emphasize this fact in the last section because we believe its omission does not hurt the
explanations there, but except for Observable and InvalidationListener, the rest of the interfaces are
generic interfaces with a type parameter . In this section, we examine how these generic interfaces are
specialized to the specific types of interest: Boolean, Integer, Long, Float, Double, String, and Object. We

93

Chapter 3 ■ Properties and Bindings

also examine some of the abstract and concrete classes of the framework and explore typical usage scenarios
of each class.

■■Note Specializations of these interfaces also exist for List, Map, and Set. They are designed for working
with observable collections. We cover observable collections in Chapter 7.

A Common Theme for Type-Specific Interfaces
Although the generic interfaces are not all specialized in exactly the same way, a common theme exists:
•

The Boolean type is specialized directly.

•

The Integer, Long, Float, and Double types are specialized through the Number
supertype.

•

The String type is specialized through the Object type.

This theme exists in the type-specific specializations of all the key interfaces. As an example, we
examine the subinterfaces of the ObservableValue interface:
•

ObservableBooleanValue extends ObservableValue, and it offers one
additional method.
•

•

•

boolean get();

ObservableNumberValue extends ObservableValue, and it offers four
additional methods.
•

int intValue();

•

long longValue();

•

float floatValue();

•

double doubleValue();

ObservableObjectValue extends ObservableValue, and it offers one
additional method.
•

T get();

•

ObservableIntegerValue, ObservableLongValue, ObservableFloatValue, and
ObservableDoubleValue extend ObservableNumberValue and each offers an
additional get() method that returns the appropriate primitive type value.

•

ObservableStringValue extends ObservableObjectValue and inherits its
get() method that returns String.

Notice that the get() method that we have been using in the examples is defined in the type-specific
ObservableValue subinterfaces. A similar examination reveals that the set() method that we have been
using in the examples is defined in the type-specific WritableValue subinterfaces.

94

Chapter 3 ■ Properties and Bindings

A practical consequence of this derivation hierarchy is that any numerical property can call bind()
on any other numerical property or binding. Indeed, the signature of the bind() method on any numerical
property is as follows:
void bind(ObservableValue  observable);
And any numerical property and binding is assignable to the generic parameter type. The program in
Listing 3-4 shows that any numerical properties of different specific types can be bound to each other.
Listing 3-4. NumericPropertiesExample.java
import
import
import
import
import
import
import
import

javafx.beans.property.DoubleProperty;
javafx.beans.property.FloatProperty;
javafx.beans.property.IntegerProperty;
javafx.beans.property.LongProperty;
javafx.beans.property.SimpleDoubleProperty;
javafx.beans.property.SimpleFloatProperty;
javafx.beans.property.SimpleIntegerProperty;
javafx.beans.property.SimpleLongProperty;

public class NumericPropertiesExample {
    public static void main(String[] args) {
        IntegerProperty i = new SimpleIntegerProperty(null, "i", 1024);
        LongProperty l = new SimpleLongProperty(null, "l", 0L);
        FloatProperty f = new SimpleFloatProperty(null, "f", 0.0F);
        DoubleProperty d = new SimpleDoubleProperty(null, "d", 0.0);
        System.out.println("Constructed numerical properties i, l, f, d.");
        System.out.println("i.get()
        System.out.println("l.get()
        System.out.println("f.get()
        System.out.println("d.get()

=
=
=
=

"
"
"
"

+
+
+
+

i.get());
l.get());
f.get());
d.get());

        l.bind(i);
        f.bind(l);
        d.bind(f);
        System.out.println("Bound l to i, f to l, d to f.");
        System.out.println("i.get()
        System.out.println("l.get()
        System.out.println("f.get()
        System.out.println("d.get()

=
=
=
=

"
"
"
"

+
+
+
+

i.get());
l.get());
f.get());
d.get());

        System.out.println("Calling i.set(2048).");
        i.set(2048);
        System.out.println("i.get()
        System.out.println("l.get()
        System.out.println("f.get()
        System.out.println("d.get()

=
=
=
=

"
"
"
"

+
+
+
+

i.get());
l.get());
f.get());
d.get());

95

Chapter 3 ■ Properties and Bindings

        d.unbind();
        f.unbind();
        l.unbind();
        System.out.println("Unbound l to i, f to l, d to f.");
        f.bind(d);
        l.bind(f);
        i.bind(l);
        System.out.println("Bound f to d, l to f, i to l.");
        System.out.println("Calling d.set(10000000000L).");
        d.set(10000000000L);
        System.out.println("d.get()
        System.out.println("f.get()
        System.out.println("l.get()
        System.out.println("i.get()
    }
}

=
=
=
=

"
"
"
"

+
+
+
+

d.get());
f.get());
l.get());
i.get());

In this example we created four numeric properties and bound them into a chain in decreasing size to
demonstrate that the bindings work as expected. We then reversed the order of the chain and set the double
property’s value to a number that would overflow the integer property to highlight the fact that even though
you can bind different sizes of numeric properties together, when the value of the dependent property is
outside the range of the binding property, normal Java numeric conversion applies.
When we run the program in Listing 3-4, the following is printed to the console:
Constructed numerical properties i, l, f, d.
i.get() = 1024
l.get() = 0
f.get() = 0.0
d.get() = 0.0
Bound l to i, f to l, d to f.
i.get() = 1024
l.get() = 1024
f.get() = 1024.0
d.get() = 1024.0
Calling i.set(2048).
i.get() = 2048
l.get() = 2048
f.get() = 2048.0
d.get() = 2048.0
Unbound l to i, f to l, d to f.
Bound f to d, l to f, i to l.
Calling d.set(10000000000L).
d.get() = 1.0E10
f.get() = 1.0E10
l.get() = 10000000000
i.get() = 1410065408

96

Chapter 3 ■ Properties and Bindings

Commonly Used Classes
We now give a survey of the content of the four packages javafx.beans, javafx.beans.binding, javafx.
beans.property, and javafx.beans.value. In this section, the SimpleIntegerProperty series of classes
refers to the classes extrapolated over the Boolean, Integer, Long, Float, Double, String, and Object types.
Therefore, what is said also applies to SimpleBooleanProperty, and so on.
•

The most often used classes in the JavaFX properties and bindings framework are the
SimpleIntegerProperty series of classes. They provide all the functionalities of the
Property interface including lazy evaluation. They are used in all the examples of
this chapter up to this point.

•

Another set of concrete classes in the JavaFX properties and bindings framework
is the ReadOnlyIntegerWrapper series of classes. These classes implement the
Property interface but also have a getReadOnlyProperty() method that returns
a ReadOnlyProperty that is synchronized with the main Property. They are very
handy to use when you need a full-blown Property for the implementation of a
component but you only want to hand out a ReadOnlyProperty to the client of the
component.

•

The IntegerPropertyBase series of abstract classes can be extended to
provide implementations of full Property classes, although in practice the
SimpleIntegerProperty series of classes is easier to use. The only abstract methods
in the IntegerPropertyBase series of classes are getBean() and getName().

•

The ReadOnlyIntegerPropertyBase series of abstract classes can be extended to
provide implementations of ReadOnlyProperty classes. This is rarely necessary. The
only abstract methods in the ReadOnlyIntegerPropertyBase series of classes are
get(), getBean(), and getName().

•

The WeakInvalidationListener and WeakChangeListener classes can be used to
wrap InvalidationListener and ChangeListener instances before addListener()
is called. They hold weak references of the wrapped listener instances. As long as
you hold a reference to the wrapped listener on your side, the weak references will
be kept alive and you will receive events. When you are done with the wrapped
listener and have unreferenced it from your side, the weak references will be eligible
for garbage collection, and then later garbage collected. All the JavaFX properties
and bindings framework Observable objects know how to clean up a weak listener
after its weak reference has been garbage collected. This prevents memory leaks
when the listeners are not removed after use. The WeakInvalidationListener
and WeakListener classes implement the WeakListener interface, whose
wasGarbageCollected() method will return true if the wrapped listener instance
was garbage collected.

That covers all the JavaFX properties and bindings APIs that reside in the javafx.beans, javafx.beans.
property, and javafx.beans.value packages and some but not all of the APIs in the javafx.beans.binding
package. The javafx.beans.property.adapters package provides adapters between old-style JavaBeans
properties and JavaFX properties. We will cover these adapters in the “Adapting JavaBeans Properties to
JavaFX Properties” section. The remaining classes of the javafx.beans.binding package are APIs that help
you to create new bindings out of existing properties and bindings. That is the focus of the next section.

97

Chapter 3 ■ Properties and Bindings

Creating Bindings
We now turn our focus to the creation of new bindings out of existing properties and bindings. You learned
in the “Understanding Key Interfaces and Concepts” section earlier in this chapter that a binding is an
observable value that has a list of dependencies that are also observable values.
The JavaFX properties and bindings framework offers three ways of creating new bindings:
•

Extending the IntegerBinding series of abstract classes.

•

Using the bindings-creating static methods in the utilities class Bindings.

•

Using the fluent interface API provided by the IntegerExpression series of abstract
classes.

You saw the direct extension approach in the “Understanding the Binding Interface” section. We
explore the Bindings utility class next.

Understanding the Bindings Utility Class
The Bindings class contains 236 factory methods that make new bindings out of existing observable values
and regular values. Most of the methods are overloaded to take into account that both observable values and
regular Java (unobservable) values can be used to build new bindings. At least one of the parameters must
be an observable value. Here are the signatures of the nine overloaded add() methods:
public
public
public
public
public
public
public
public
public

static
static
static
static
static
static
static
static
static

NumberBinding
DoubleBinding
DoubleBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding

add(ObservableNumberValue n1, ObservableNumberValue n2)
add(ObservableNumberValue n, double d)
add(double d, ObservableNumberValue n)
add(ObservableNumberValue n, float f)
add(float f, ObservableNumberValue n)
add(ObservableNumberValue n, long l)
add(long l, ObservableNumberValue n)
add(ObservableNumberValue n, int i)
add(int i, ObservableNumberValue n)

When the add() method is called, it returns a NumberBinding with dependencies that include all the
observable value parameters, and whose value is the sum of the value of its two parameters. Similarly
overloaded methods exist for subtract(), multiply(), and divide().

■■Note

Recall from the last section that ObservableIntegerValue, ObservableLongValue,
ObservableFloatValue, and ObservableDoubleValue are subclasses of ObservableNumberValue. Therefore,
the four arithmetic methods just mentioned can take any combinations of these observable numeric values as
well as any unobservable values.
The program in Listing 3-5 uses the arithmetic methods in Bindings to calculate the area of a triangle in
the Cartesian plane with vertices (x1, y1), (x2, y2), (x3, y3) using this formula:
Area = (x1*y2 + x2*y3 + x3*y1 – x1*y3 – x2*y1 – x3*y2) / 2

98

Chapter 3 ■ Properties and Bindings

Listing 3-5. TriangleAreaExample.java
import
import
import
import

javafx.beans.binding.Bindings;
javafx.beans.binding.NumberBinding;
javafx.beans.property.IntegerProperty;
javafx.beans.property.SimpleIntegerProperty;

public class TriangleAreaExample {
    public static void main(String[] args) {
        IntegerProperty x1 = new SimpleIntegerProperty(0);
        IntegerProperty y1 = new SimpleIntegerProperty(0);
        IntegerProperty x2 = new SimpleIntegerProperty(0);
        IntegerProperty y2 = new SimpleIntegerProperty(0);
        IntegerProperty x3 = new SimpleIntegerProperty(0);
        IntegerProperty y3 = new SimpleIntegerProperty(0);
        final
        final
        final
        final
        final
        final

NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding

x1y2
x2y3
x3y1
x1y3
x2y1
x3y2

=
=
=
=
=
=

Bindings.multiply(x1,
Bindings.multiply(x2,
Bindings.multiply(x3,
Bindings.multiply(x1,
Bindings.multiply(x2,
Bindings.multiply(x3,

y2);
y3);
y1);
y3);
y1);
y2);

        final
        final
        final
        final
        final
        final
        final

NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding
NumberBinding

sum1 = Bindings.add(x1y2, x2y3);
sum2 = Bindings.add(sum1, x3y1);
sum3 = Bindings.add(sum2, x3y1);
diff1 = Bindings.subtract(sum3, x1y3);
diff2 = Bindings.subtract(diff1, x2y1);
determinant = Bindings.subtract(diff2, x3y2);
area = Bindings.divide(determinant, 2.0D);

        x1.set(0); y1.set(0);
        x2.set(6); y2.set(0);
        x3.set(4); y3.set(3);
        printResult(x1, y1, x2, y2, x3, y3, area);
        x1.set(1); y1.set(0);
        x2.set(2); y2.set(2);
        x3.set(0); y3.set(1);
        printResult(x1, y1, x2, y2, x3, y3, area);
    }
    private static void printResult(IntegerProperty x1,
                                    IntegerProperty x2,
                                    IntegerProperty x3,
                                    NumberBinding area)
        System.out.println("For A(" +

IntegerProperty y1,
IntegerProperty y2,
IntegerProperty y3,
{

99

Chapter 3 ■ Properties and Bindings

                x1.get() + "," + y1.get() + "), B(" +
                x2.get() + "," + y2.get() + "), C(" +
                x3.get() + "," + y3.get() + "), the area of triangle ABC is " + area.
getValue());
    }
}
We used IntegerProperty to represent the coordinates. The building up of the NumberBinding area
uses all four arithmetic factory methods of Bindings. Because we started with IntegerProperty objects,
even though the return type from the arithmetic factory methods of Bindings is NumberBinding, the actual
objects that are returned, up to determinant, are IntegerBinding objects. We used 2.0D rather than a
mere 2 in the divide() call to force the division to be done as a double division, not as int division. All
the properties and bindings that we build up form a tree structure with area as the root, the intermediate
bindings as internal nodes, and the properties x1, y1, x2, y2, x3, y3 as leaves. This tree is similar to the parse
tree we will get if we parse the mathematical expression for the area formula using grammar for the regular
arithmetic expressions.
When we run the program in Listing 3-5, the following output is printed to the console:
For A(0,0), B(6,0), C(4,3), the area of triangle ABC is 9.0
For A(1,0), B(2,2), C(0,1), the area of triangle ABC is 1.5
Aside from the arithmetic methods, the Bindings class also has the following factory methods.
•

Logical operators: and, or, not

•

Numeric operators: min, max, negate

•

Object operators: isNull, isNotNull

•

String operators: length, isEmpty, isNotEmpty

•

Relational operators:

•

100

•

equal

•

equalIgnoreCase

•

greaterThan

•

greaterThanOrEqual

•

lessThan

•

lessThanOrEqual

•

notEqual

•

notEqualIgnoreCase

Creation operators:
•

createBooleanBinding

•

createIntegerBinding

•

createLongBinding

•

createFloatBinding

Chapter 3 ■ Properties and Bindings

•

•

createDoubleBinding

•

createStringBinding

•

createObjectBinding

Selection operators:
•

select

•

selectBoolean

•

selectInteger

•

selectLong

•

selectFloat

•

selectDouble

•

selectString

Except for the creation operators and the selection operators, the preceding operators all do what you
think they will do. The object operators are meaningful only for observable string values and observable
object values. The string operators are meaningful only for observable string values. All relational operators
except for the IgnoreCase ones apply to numeric values. There are versions of the equal and notEqual
operators for numeric values that have a third double parameter for the tolerance when comparing float or
double values. The equal and notEqual operators also apply to boolean, string, and object values. For string
and object values, the equal and notEqual operator compares their values using the equals() method.
The creation operators provide a convenient way of creating a binding without directly extending the
abstract base class. It takes a Callable and any number of dependencies as an argument. The area double
binding in Listing 3-3 can be rewritten using a lambda expression as the Callable, as follows,:
DoubleBinding area = Bindings.createDoubleBinding(() -> {
    return x.get() * y.get();
}, x, y);
The selection operators operate on what are called JavaFX Beans, Java classes constructed according
to the JavaFX Beans specification. We talk about JavaFX Beans in the “Understanding JavaFX Beans
Convention” section later in this chapter.
There are a number of methods in Bindings that deal with observable collections. We cover them in
Chapter 7.
That covers all methods in Bindings that return a binding object. There are 18 methods in Bindings that
do not return a binding object. The various bindBidirectional() and unbindBidirectional() methods
create bidirectional bindings. As a matter of fact, the bindBidirectional() and unbindBidirectional()
methods in the various properties classes simply call the corresponding ones in the Bindings class. The
bindContent() and unbindContent() methods bind an ordinary collection to an observable collection. The
convert(), concat(), and a pair of overloaded format() methods return StringExpression objects. And
finally, the when() method returns a When object.
The When and the StringExpression classes are part of the fluent interface API for creating bindings,
which we cover in the next subsection.

101

Chapter 3 ■ Properties and Bindings

Understanding the Fluent Interface API
If you asked, “Why would anybody name a method when()?” and “What kind of information would the When
class encapsulate?”—welcome to the club. While you were not looking, the object-oriented programming
community invented a brand-new method of API design that totally disregards the decades-old principles of
object-oriented practices. Instead of encapsulating data and distributing business logic into relevant domain
objects, this new methodology produces a style of API that encourages method chaining and uses the return
type of one method to determine what methods are available for the next car of the choo-choo train. Method
names are chosen not to convey complete meaning, but to make the entire method chain read like a fluent
sentence. This style is called a fluent interface API.

■■Note You can find a more thorough exposition of fluent interfaces on Martin Fowler’s web site, referenced
at the end of this chapter.
The fluent interface APIs for creating bindings are defined in the IntegerExpression series of classes.
IntegerExpression is a superclass of both IntegerProperty and IntegerBinding, making the methods of
IntegerExpression also available in the IntegerProperty and IntegerBinding classes. The four numeric
expression classes share a common superinterface NumberExpression, where all the methods are defined.
The type-specific expression classes override some of the methods that yield a NumberBinding to return a
more appropriate type of binding.
The methods thus made available for the seven kinds of properties and bindings are listed here:

•

102

•

For BooleanProperty and BooleanBindingBooleanBinding
and(ObservableBooleanValue b)

•

BooleanBinding or(ObservableBooleanValue b)

•

BooleanBinding not()

•

BooleanBinding isEqualTo(ObservableBooleanValue b)

•

BooleanBinding isNotEqualTo(ObservableBooleanValue b)

•

StringBinding asString()

Common for all numeric properties and bindings
•

BooleanBinding isEqualTo(ObservableNumberValue m)

•

BooleanBinding isEqualTo(ObservableNumberValue m, double err)

•

BooleanBinding isEqualTo(double d, double err)

•

BooleanBinding isEqualTo(float f, double err)

•

BooleanBinding isEqualTo(long l)

•

BooleanBinding isEqualTo(long l, double err)

•

BooleanBinding isEqualTo(int i)

•

BooleanBinding isEqualTo(int i, double err)

•

BooleanBinding isNotEqualTo(ObservableNumberValue m)

•

BooleanBinding isNotEqualTo(ObservableNumberValue m, double err)

Chapter 3 ■ Properties and Bindings

•

•

BooleanBinding isNotEqualTo(double d, double err)

•

BooleanBinding isNotEqualTo(float f, double err)

•

BooleanBinding isNotEqualTo(long l)

•

BooleanBinding isNotEqualTo(long l, double err)

•

BooleanBinding isNotEqualTo(int i)

•

BooleanBinding isNotEqualTo(int i, double err)

•

BooleanBinding greaterThan(ObservableNumberValue m)

•

BooleanBinding greaterThan(double d)

•

BooleanBinding greaterThan(float f)

•

BooleanBinding greaterThan(long l)

•

BooleanBinding greaterThan(int i)

•

BooleanBinding lessThan(ObservableNumberValue m)

•

BooleanBinding lessThan(double d)

•

BooleanBinding lessThan(float f)

•

BooleanBinding lessThan(long l)

•

BooleanBinding lessThan(int i)

•

BooleanBinding greaterThanOrEqualTo(ObservableNumberValue m)

•

BooleanBinding greaterThanOrEqualTo(double d)

•

BooleanBinding greaterThanOrEqualTo(float f)

•

BooleanBinding greaterThanOrEqualTo(long l)

•

BooleanBinding greaterThanOrEqualTo(int i)

•

BooleanBinding lessThanOrEqualTo(ObservableNumberValue m)

•

BooleanBinding lessThanOrEqualTo(double d)

•

BooleanBinding lessThanOrEqualTo(float f)

•

BooleanBinding lessThanOrEqualTo(long l)

•

BooleanBinding lessThanOrEqualTo(int i)

•

StringBinding asString()

•

StringBinding asString(String str)

•

StringBinding asString(Locale locale, String str)

For IntegerProperty and IntegerBinding
•

IntegerBinding negate()

•

NumberBinding add(ObservableNumberValue n)

•

DoubleBinding add(double d)

103

Chapter 3 ■ Properties and Bindings

•

104

•

FloatBinding add(float f)

•

LongBinding add(long l)

•

IntegerBinding add(int i)

•

NumberBinding subtract(ObservableNumberValue n)

•

DoubleBinding subtract(double d)

•

FloatBinding subtract(float f)

•

LongBinding subtract(long l)

•

IntegerBinding subtract(int i)

•

NumberBinding multiply(ObservableNumberValue n)

•

DoubleBinding multiply(double d)

•

FloatBinding multiply(float f)

•

LongBinding multiply(long l)

•

IntegerBinding multiply(int i)

•

NumberBinding divide(ObservableNumberValue n)

•

DoubleBinding divide(double d)

•

FloatBinding divide(float f)

•

LongBinding divide(long l)

•

IntegerBinding divide(int i)

For LongProperty and LongBinding
•

LongBinding negate()

•

NumberBinding add(ObservableNumberValue n)

•

DoubleBinding add(double d)

•

FloatBinding add(float f)

•

LongBinding add(long l)

•

LongBinding add(int i)

•

NumberBinding subtract(ObservableNumberValue n)

•

DoubleBinding subtract(double d)

•

FloatBinding subtract(float f)

•

LongBinding subtract(long l)

•

LongBinding subtract(int i)

•

NumberBinding multiply(ObservableNumberValue n)

•

DoubleBinding multiply(double d)

•

FloatBinding multiply(float f)

Chapter 3 ■ Properties and Bindings

•

•

•

LongBinding multiply(long l)

•

LongBinding multiply(int i)

•

NumberBinding divide(ObservableNumberValue n)

•

DoubleBinding divide(double d)

•

FloatBinding divide(float f)

•

LongBinding divide(long l)

•

LongBinding divide(int i)

For FloatProperty and FloatBinding
•

FloatBinding negate()

•

NumberBinding add(ObservableNumberValue n)

•

DoubleBinding add(double d)

•

FloatBinding add(float g)

•

FloatBinding add(long l)

•

FloatBinding add(int i)

•

NumberBinding subtract(ObservableNumberValue n)

•

DoubleBinding subtract(double d)

•

FloatBinding subtract(float g)

•

FloatBinding subtract(long l)

•

FloatBinding subtract(int i)

•

NumberBinding multiply(ObservableNumberValue n)

•

DoubleBinding multiply(double d)

•

FloatBinding multiply(float g)

•

FloatBinding multiply(long l)

•

FloatBinding multiply(int i)

•

NumberBinding divide(ObservableNumberValue n)

•

DoubleBinding divide(double d)

•

FloatBinding divide(float g)

•

FloatBinding divide(long l)

•

FloatBinding divide(int i)

For DoubleProperty and DoubleBinding
•

DoubleBinding negate()

•

DoubleBinding add(ObservableNumberValue n)

•

DoubleBinding add(double d)

105

Chapter 3 ■ Properties and Bindings

•

106

•

DoubleBinding add(float f)

•

DoubleBinding add(long l)

•

DoubleBinding add(int i)

•

DoubleBinding subtract(ObservableNumberValue n)

•

DoubleBinding subtract(double d)

•

DoubleBinding subtract(float f)

•

DoubleBinding subtract(long l)

•

DoubleBinding subtract(int i)

•

DoubleBinding multiply(ObservableNumberValue n)

•

DoubleBinding multiply(double d)

•

DoubleBinding multiply(float f)

•

DoubleBinding multiply(long l)

•

DoubleBinding multiply(int i)

•

DoubleBinding divide(ObservableNumberValue n)

•

DoubleBinding divide(double d)

•

DoubleBinding divide(float f)

•

DoubleBinding divide(long l)

•

DoubleBinding divide(int i)

For StringProperty and StringBinding
•

StringExpression concat(Object obj)

•

BooleanBinding isEqualTo(ObservableStringValue str)

•

BooleanBinding isEqualTo(String str)

•

BooleanBinding isNotEqualTo(ObservableStringValue str)

•

BooleanBinding isNotEqualTo(String str)

•

BooleanBinding isEqualToIgnoreCase(ObservableStringValue str)

•

BooleanBinding isEqualToIgnoreCase(String str)

•

BooleanBinding isNotEqualToIgnoreCase(ObservableStringValue str)

•

BooleanBinding isNotEqualToIgnoreCase(String str)

•

BooleanBinding greaterThan(ObservableStringValue str)

•

BooleanBinding greaterThan(String str)

•

BooleanBinding lessThan(ObservableStringValue str)

•

BooleanBinding lessThan(String str)

•

BooleanBinding greaterThanOrEqualTo(ObservableStringValue str)

Chapter 3 ■ Properties and Bindings

•

•

BooleanBinding greaterThanOrEqualTo(String str)

•

BooleanBinding lessThanOrEqualTo(ObservableStringValue str)

•

BooleanBinding lessThanOrEqualTo(String str)

•

BooleanBinding isNull()

•

BooleanBinding isNotNull()

•

IntegerBinding length()

•

BooleanExpression isEmpty()

•

BooleanExpression isNotEmpty()

For ObjectProperty and ObjectBinding
•

BooleanBinding isEqualTo(ObservableObjectValue obj)

•

BooleanBinding isEqualTo(Object obj)

•

BooleanBinding isNotEqualTo(ObservableObjectValue obj)

•

BooleanBinding isNotEqualTo(Object obj)

•

BooleanBinding isNull()

•

BooleanBinding isNotNull()

With these methods, you can create an infinite variety of bindings by starting with a property and
calling one of the methods that is appropriate for the type of the property to get a binding, and calling one
of the methods that is appropriate for the type of the binding to get another binding, and so on. One fact
that is worth pointing out here is that all the methods for the type-specific numeric expressions are defined
in the NumberExpression base interface with a return type of NumberBinding, and are overridden in the
type-specific expression classes with an identical parameter signature but a more specific return type. This
way of overriding a method in a subclass with an identical parameter signature but a more specific return
type is called covariant return-type overriding, and has been a Java language feature since Java 5. One of
the consequences of this fact is that numeric bindings built with the fluent interface API have more specific
types than those built with factory methods in the Bindings class.
Sometimes it is necessary to convert a type-specific expression into an object expression holding the
same type of value. This can be done with the asObject() method in the type-specific expression class. The
conversion back can be done using static methods in the expressions class. For IntegerExpression, these
static methods are as follows:
static IntegerExpression integerExpression(ObservableIntegerValue value)
static  IntegerExpression integerExpression(ObservableValue value)
The program in Listing 3-6 is a modification of the triangle area example in Listing 3-5 that uses the
fluent interface API instead of calling factory methods in the Bindings class.
Listing 3-6. TriangleAreaFluentExample.java
import
import
import
import
import

javafx.beans.binding.Bindings;
javafx.beans.binding.NumberBinding;
javafx.beans.binding.StringExpression;
javafx.beans.property.IntegerProperty;
javafx.beans.property.SimpleIntegerProperty;

107

Chapter 3 ■ Properties and Bindings

public class TriangleAreaFluentExample {
    public static void main(String[] args) {
        IntegerProperty x1 = new SimpleIntegerProperty(0);
        IntegerProperty y1 = new SimpleIntegerProperty(0);
        IntegerProperty x2 = new SimpleIntegerProperty(0);
        IntegerProperty y2 = new SimpleIntegerProperty(0);
        IntegerProperty x3 = new SimpleIntegerProperty(0);
        IntegerProperty y3 = new SimpleIntegerProperty(0);
        final NumberBinding area = x1.multiply(y2)
                .add(x2.multiply(y3))
                .add(x3.multiply(y1))
                .subtract(x1.multiply(y3))
                .subtract(x2.multiply(y1))
                .subtract(x3.multiply(y2))
                .divide(2.0D);
        StringExpression output = Bindings.format(
                "For A(%d,%d), B(%d,%d), C(%d,%d), the area of triangle ABC is %3.1f",
                x1, y1, x2, y2, x3, y3, area);
        x1.set(0); y1.set(0);
        x2.set(6); y2.set(0);
        x3.set(4); y3.set(3);
        System.out.println(output.get());
        x1.set(1); y1.set(0);
        x2.set(2); y2.set(2);
        x3.set(0); y3.set(1);
        System.out.println(output.get());
    }
}
Notice how the 13 lines of code and 12 intermediate variables used in Listing 3-5 to build up the area
binding are reduced to the 7 lines of code with no intermediate variables used in Listing 3-6. We also used
the Bindings.format() method to build up a StringExpression object called output. There are two
overloaded Bindings.format() methods with signatures:
StringExpression format(Locale locale, String format, Object... args)
StringExpression format(String format, Object... args)
They work similarly to the corresponding String.format() methods in that they format the values args
according to the format specification format and the Locale locale, or the default Locale. If any of the args
is an ObservableValue, its change is reflected in the StringExpression.
When we run the program in Listing 3-6, the following output is printed to the console:
For A(0,0), B(6,0), C(4,3), the area of triangle ABC is 9.0
For A(1,0), B(2,2), C(0,1), the area of triangle ABC is 1.5

108

Chapter 3 ■ Properties and Bindings

Next we unravel the mystery of the When class and the role it plays in constructing bindings that are
essentially if/then/else expressions. The When class has a constructor that takes an ObservableBooleanValue
argument:
public When(ObservableBooleanValue b)
It has the following 11 overloaded then() methods.
When.NumberConditionBuilder then(ObservableNumberValue n)
When.NumberConditionBuilder then(double d)
When.NumberConditionBuilder then(float f)
When.NumberConditionBuilder then(long l)
When.NumberConditionBuilder then(int i)
When.BooleanConditionBuilder then(ObservableBooleanValue b)
When.BooleanConditionBuilder then(boolean b)
When.StringConditionBuilder then(ObservableStringValue str)
When.StringConditionBuilder then(String str)
When.ObjectConditionBuilder then(ObservableObjectValue obj)
When.ObjectConditionBuilder then(T obj)
The type of object returned from the then() method depends on the type of the argument.
If the argument is a numeric type, either observable or unobservable, the return type is a nested
class When.NumberConditionBuilder. Similarly, for Boolean arguments, the return type is When.
BooleanConditionBuilder; for string arguments, When.StringConditionBuilder; and for object arguments,
When.ObjectConditionBuilder.
These condition builders in turn have the following otherwise() methods.
•

•

•

•

For When.NumberConditionBuilder
•

NumberBinding otherwise(ObservableNumberValue n)

•

DoubleBinding otherwise(double d)

•

NumberBinding otherwise(float f)

•

NumberBinding otherwise(long l)

•

NumberBinding otherwise(int i)

For When.BooleanConditionBuilder
•

BooleanBinding otherwise(ObservableBooleanValue b)

•

BooleanBinding otherwise(boolean b)

For When.StringConditionBuilder
•

StringBinding otherwise(ObservableStringValue str)

•

StringBinding otherwise(String str)

For When.ObjectConditionBuilder
•

ObjectBinding otherwise(ObservableObjectValue

•

ObjectBinding otherwise(T obj)

obj)

109

Chapter 3 ■ Properties and Bindings

The net effect of these method signatures is that you can build up a binding that resembles an if/then/
else expression this way:
new When(b).then(x).otherwise(y)
b is an ObservableBooleanValue, and x and y are of similar types and can be either observable or
unobservable. The resulting binding will be of a type similar to that of x and y.
The program in Listing 3-7 uses the fluent interface API from the When class to calculate the area of a
triangle with given sides a, b, and c. Recall that to form a triangle, the three sides must satisfy the following
conditions:
a + b > c, b + c > a, c + a > b.
When the preceding conditions are satisfied, the area of the triangle can be calculated using Heron’s
formula:
Area = sqrt(s * (s – a) * (s – b) * (s – c))
where s is the semiperimeter:
s = (a + b + c) / 2.
Listing 3-7. HeronsFormulaExample.java
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.When;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class HeronsFormulaExample {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(0);
        DoubleProperty b = new SimpleDoubleProperty(0);
        DoubleProperty c = new SimpleDoubleProperty(0);
        DoubleBinding s = a.add(b).add(c).divide(2.0D);
        final DoubleBinding areaSquared = new When(
                        a.add(b).greaterThan(c)
                        .and(b.add(c).greaterThan(a))
                        .and(c.add(a).greaterThan(b)))
                .then(s.multiply(s.subtract(a))
                        .multiply(s.subtract(b))
                        .multiply(s.subtract(c)))
                .otherwise(0.0D);
        a.set(3);
        b.set(4);
        c.set(5);
        System.out.printf("Given sides a = %1.0f, b = %1.0f, and c = %1.0f," +

110

Chapter 3 ■ Properties and Bindings

                " the area of the triangle is %3.2f\n", a.get(), b.get(), c.get(),
                Math.sqrt(areaSquared.get()));
        a.set(2);
        b.set(2);
        c.set(2);
        System.out.printf("Given sides a = %1.0f, b = %1.0f, and c = %1.0f," +
                " the area of the triangle is %3.2f\n", a.get(), b.get(), c.get(),
                Math.sqrt(areaSquared.get()));
    }
}
Inasmuch as there is no ready-made binding method in DoubleExpression that calculates the square
root, we create a DoubleBinding for areaSquared instead. The constructor argument for When() is a
BooleanBinding built out of the three conditions on a, b, and c. The argument for the then() method is a
DoubleBinding that calculates the square of the area of the triangle. And because the then() argument is
numeric, the otherwise() argument also has to be numeric. We choose to use 0.0D to signal that an invalid
triangle is encountered.

■■Note

Instead of using the When() constructor, you can also use the factory method when() in the
Bindings utility class to create the When object.
When we run the program in Listing 3-7, the following output is printed to the console:
Given sides a = 3, b = 4, and c = 5, the area of the triangle is 6.00.
Given sides a = 2, b = 2, and c = 2, the area of the triangle is 1.73.
If the binding defined in Listing 3-7 makes your head spin a little, you are not alone. We choose this
example simply to illustrate the use of the fluent interface API offered by the When class. As a matter of
fact, this example might be better served with a direct subclassing approach we first introduced in the
“Understanding the Binding Interface” section.
The program in Listing 3-8 solves the same problem as Listing 3-7 by using the direct extension method.
Listing 3-8. HeronsFormulaDirectExtensionExample.java
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
public class HeronsFormulaDirectExtensionExample {
    public static void main(String[] args) {
        final DoubleProperty a = new SimpleDoubleProperty(0);
        final DoubleProperty b = new SimpleDoubleProperty(0);
        final DoubleProperty c = new SimpleDoubleProperty(0);

111

Chapter 3 ■ Properties and Bindings

        DoubleBinding area = new DoubleBinding() {
            {
                super.bind(a, b, c);
            }
            @Override
            protected double computeValue() {
                double a0 = a.get();
                double b0 = b.get();
                double c0 = c.get();
                if ((a0 + b0 > c0) && (b0 + c0 > a0) && (c0 + a0 > b0)) {
                    double s = (a0 + b0 + c0) / 2.0D;
                    return Math.sqrt(s * (s - a0) * (s - b0) * (s - c0));
                } else {
                    return 0.0D;
                }
            }
        };
        a.set(3);
        b.set(4);
        c.set(5);
        System.out.printf("Given sides a = %1.0f, b = %1.0f, and c = %1.0f," +
                " the area of the triangle is %3.2f\n", a.get(), b.get(), c.get(),
                area.get());
        a.set(2);
        b.set(2);
        c.set(2);
        System.out.printf("Given sides a = %1.0f, b = %1.0f, and c = %1.0f," +
                " the area of the triangle is %3.2f\n", a.get(), b.get(), c.get(),
                area.get());
    }
}
The direct extension method is preferred for complicated expressions and for expressions that go
beyond the available operators.
Now that you have mastered all the APIs in the javafx.beans, javafx.beans.binding, javafx.beans.
property, and javafx.beans.value packages, you are ready to step beyond the details of the JavaFX
properties and bindings framework and learn how these properties are organized into bigger components
called JavaFX Beans.

Understanding the JavaFX Beans Convention
JavaFX introduces the concept of JavaFX Beans, a set of conventions that provide properties support for Java
objects. In this section, we talk about the naming conventions for specifying JavaFX Beans properties, several
ways of implementing JavaFX Beans properties, and finally the use of selection bindings.

112

Chapter 3 ■ Properties and Bindings

The JavaFX Beans Specification
For many years Java has used the JavaBeans API to represent a property of an object. A JavaBeans property
is represented by a pair of getter and setter methods. Property changes are propagated to property change
listeners through the firing of property change events in the setter code.
JavaFX introduces the JavaFX Beans specification that adds properties support to Java objects through
the help of the properties classes from the JavaFX properties and bindings framework.

■■Caution The word property is used here with two distinct meanings. When we say, “JavaFX Beans
properties,” it should be understood to mean a higher-level concept similar to JavaBeans properties. When
we say, “JavaFX properties and bindings framework properties,” it should be understood to mean the
various implementations of the Property or ReadOnlyProperty interfaces, such as IntegerProperty,
StringProperty, and so on. JavaFX Beans properties are specified using the JavaFX properties and bindings
framework properties.
Like their JavaBeans counterparts, JavaFX Beans properties are specified by a set of methods in a Java
class. To define a JavaFX Beans property in a Java class, you provide three methods: the getter, the setter, and
the property getter. For a property named height of type double, the three methods are:
public final double getHeight();
public final void setHeight(double h);
public DoubleProperty heightProperty();
The names of the getter and setter methods follow the JavaBeans convention. They are obtained
by concatenating “get” and “set” with the name of the property with the first character capitalized. For
boolean type properties, the getter name can also start with “is”. The name of the property getter is obtained
by concatenating the name of the property with “Property”. To define a read-only JavaFX Beans property,
you can either remove the setter method or change it to a private method and change the return type of the
property getter to be a ReadOnlyProperty.
This specification speaks only about the interface of JavaFX Beans properties and does not impose
any implementation constraints. Depending on the number of properties a JavaFX Bean may have, and
the usage patterns of these properties, there are several implementation strategies. Not surprisingly, all of
them use the JavaFX properties and bindings framework properties as the backing store for the values of the
JavaFX Beans properties. We show you these strategies in the next two subsections.

Understanding the Eagerly Instantiated Properties Strategy
The eagerly instantiated properties strategy is the simplest way to implement JavaFX Beans properties. For
every JavaFX Beans property you want to define in an object, you introduce a private field in the class that
is of the appropriate JavaFX properties and bindings framework property type. These private fields are
instantiated at bean construction time. The getter and setter methods simply call the private field’s get()
and set() methods. The property getter simply returns the private field itself.
The program in Listing 3-9 defines a JavaFX Bean with an int property i, a String property str, and a
Color property color.

113

Chapter 3 ■ Properties and Bindings

Listing 3-9. JavaFXBeanModelExample.java
import
import
import
import
import
import
import

javafx.beans.property.IntegerProperty;
javafx.beans.property.ObjectProperty;
javafx.beans.property.SimpleIntegerProperty;
javafx.beans.property.SimpleObjectProperty;
javafx.beans.property.SimpleStringProperty;
javafx.beans.property.StringProperty;
javafx.scene.paint.Color;

public class JavaFXBeanModelExample {
    private IntegerProperty i = new SimpleIntegerProperty(this, "i", 0);
    private StringProperty str = new SimpleStringProperty(this, "str", "Hello");
    private ObjectProperty color = new SimpleObjectProperty(this, "color",
Color.BLACK);
    public final int getI() {
        return i.get();
    }
    public final void setI(int i) {
        this.i.set(i);
    }
    public IntegerProperty iProperty() {
        return i;
    }
    public final String getStr() {
        return str.get();
    }
    public final void setStr(String str) {
        this.str.set(str);
    }
    public StringProperty strProperty() {
        return str;
    }
    public final Color getColor() {
        return color.get();
    }
    public final void setColor(Color color) {
        this.color.set(color);
    }
    public ObjectProperty colorProperty() {
        return color;
    }
}

114

Chapter 3 ■ Properties and Bindings

This is a straightforward Java class. There are only two things we want to point out in this
implementation. First, the getter and setter methods are declared final by convention. Second, when the
private fields are initialized, we called the simple properties constructors with the full context information,
supplying them with this as the first parameter. In all of our previous examples in this chapter, we used
null as the first parameter for the simple properties constructors because those properties are not part of a
higher-level JavaFX Bean object.
The program in Listing 3-10 defines a view class that watches over an instance of the JavaFX Bean
defined in Listing 3-9. It observes changes to the i, str, and color properties of the bean by hooking up
change listeners that print out any changes to the console.
Listing 3-10. JavaFXBeanViewExample.java
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.paint.Color;
public class JavaFXBeanViewExample {
    private JavaFXBeanModelExample model;
    public JavaFXBeanViewExample(JavaFXBeanModelExample model) {
        this.model = model;
        hookupChangeListeners();
    }
    private void hookupChangeListeners() {
        model.iProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observableValue, Number
            oldValue, Number newValue) {
                System.out.println("Property i changed: old value = " + oldValue + ", new
                value = " + newValue);
            }
        });
        model.strProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observableValue, String
            oldValue, String newValue) {
                System.out.println("Property str changed: old value = " + oldValue + ", new
                value = " + newValue);
            }
        });
        model.colorProperty().addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observableValue, Color
            oldValue, Color newValue) {
                System.out.println("Property color changed: old value = " + oldValue + ",
                new value = " + newValue);
            }
        });
    }
}

115

Chapter 3 ■ Properties and Bindings

The program in Listing 3-11 defines a controller that can modify a model object.
Listing 3-11. JavaFXBeanControllerExample.java
import javafx.scene.paint.Color;
public class JavaFXBeanControllerExample {
    private JavaFXBeanModelExample model;
    private JavaFXBeanViewExample view;
    public JavaFXBeanControllerExample(JavaFXBeanModelExample model,
JavaFXBeanViewExampleÉ
view) {
        this.model = model;
        this.view = view;
    }
    public void incrementIPropertyOnModel() {
        model.setI(model.getI() + 1);
    }
    public void changeStrPropertyOnModel() {
        final String str = model.getStr();
        if (str.equals("Hello")) {
            model.setStr("World");
        } else {
            model.setStr("Hello");
        }
    }
    public void switchColorPropertyOnModel() {
        final Color color = model.getColor();
        if (color.equals(Color.BLACK)) {
            model.setColor(Color.WHITE);
        } else {
            model.setColor(Color.BLACK);
        }
    }
}
Notice that this is not a full-blown controller and does not do anything with its reference to the view
object. The program in Listing 3-12 provides a main program that assembles and test drives the classes in
Listings 3-9 to 3-11 in a typical model–view–controller pattern.

116

Chapter 3 ■ Properties and Bindings

Listing 3-12. JavaFXbeanMainExample.java
public class JavaFXBeanMainExample {
    public static void main(String[] args) {
        JavaFXBeanModelExample model = new JavaFXBeanModelExample();
        JavaFXBeanViewExample view = new JavaFXBeanViewExample(model);
        
JavaFXBeanControllerExample controller = new JavaFXBeanControllerExample(model, view);
        controller.incrementIPropertyOnModel();
        controller.changeStrPropertyOnModel();
        controller.switchColorPropertyOnModel();
        controller.incrementIPropertyOnModel();
        controller.changeStrPropertyOnModel();
        controller.switchColorPropertyOnModel();
    }
}
When we run the program in Listings 3-9 to 3-12, the following output is printed to the console:
Property
Property
Property
Property
Property
Property

i changed: old value = 0, new value = 1
str changed: old value = Hello, new value = World
color changed: old value = 0x000000ff, new value = 0xffffffff
i changed: old value = 1, new value = 2
str changed: old value = World, new value = Hello
color changed: old value = 0xffffffff, new value = 0x000000ff

Understanding the Lazily Instantiated Properties Strategy
If your JavaFX Bean has many properties, instantiating all the properties objects up front at bean creation
time may be too heavy an approach. The memory for all the properties objects is truly wasted if only a few of
the properties are actually used. In such situations, you can use one of several lazily instantiated properties
strategies. Two typical such strategies are the half-lazy instantiation strategy and the full-lazy instantiation
strategy.
In the half-lazy strategy, the property object is instantiated only if the setter is called with a value that is
different from the default value, or if the property getter is called. The program in Listing 3-13 illustrates how
this strategy is implemented.
Listing 3-13. JavaFXBeanModelHalfLazyExample.java
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class JavaFXBeanModelHalfLazyExample {
    private static final String DEFAULT_STR = "Hello";
    private StringProperty str;
    public final String getStr() {
        if (str != null) {
            return str.get();
        } else {

117

Chapter 3 ■ Properties and Bindings

            return DEFAULT_STR;
        }
    }
    public final void setStr(String str) {
        if ((this.str != null) || !(str.equals(DEFAULT_STR))) {
            strProperty().set(str);
        }
    }
    public StringProperty strProperty() {
        if (str == null) {
            str = new SimpleStringProperty(this, "str", DEFAULT_STR);
        }
        return str;
    }
}
In this strategy, the client code can call the getter many times without the property object being
instantiated. If the property object is null, the getter simply returns the default value. As soon as the setter
is called with a value that is different from the default value, it will call the property getter, which lazily
instantiates the property object. The property object is also instantiated if the client code calls the property
getter directly.
In the full-lazy strategy, the property object is instantiated only if the property getter is called. The
getter and setter go through the property object only if it is already instantiated; otherwise, they go through a
separate field.
The program in Listing 3-14 shows an example of a full-lazy property.
Listing 3-14. JavaFXBeanModelFullLazyExample.java
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class JavaFXBeanModelFullLazyExample {
    private static final String DEFAULT_STR = "Hello";
    private StringProperty str;
    private String _str = DEFAULT_STR;
    public final String getStr() {
        if (str != null) {
            return str.get();
        } else {
            return _str;
        }
    }
    public final void setStr(String str) {
        if (this.str != null) {
            this.str.set(str);
        } else {
            _str = str;
        }
    }

118

Chapter 3 ■ Properties and Bindings

    public StringProperty strProperty() {
        if (str == null) {
            str = new SimpleStringProperty(this, "str", _str);
        }
        return str;
    }
}

■■Caution The full-lazy instantiation strategy incurs the cost of an extra field to stave off the need for
property instantiation a little longer. Similarly, both the half-lazy and the full-lazy instantiation strategies incur
costs of implementation complexity and runtime performance to gain the benefit of a potentially reduced
runtime memory footprint. This is a classic trade-off situation in software engineering. Which strategy you
choose will depend on the circumstance of your application. Our advice is to introduce optimization only if there
is a need.

Using Selection Bindings
As you saw in the “Understanding the Bindings Utility Class” section, the Bindings utility class contains
seven selection operators. The method signatures of these operators are:
•

select(Object root, String… steps)

•

selectBoolean(Object root, String… steps)

•

selectDouble(Object root, String… steps)

•

selectFloat(Object root, String… steps)

•

selectInteger(Object root, String… steps)

•

selectLong(Object root, String… steps)

•

selectString(Object root, String… steps)

These selection operators allow you to create bindings that observe deeply nested JavaFX Beans
properties. Suppose that you have a JavaFX bean that has a property, whose type is a JavaFX bean that has
a property, whose type is a JavaFX bean that has a property, and so on. Suppose also that you are observing
the root of this properties chain through an ObjectProperty. You can then create a binding that observes
the deeply nested JavaFX Beans property by calling one of the select methods whose type matches the type
of the deeply nested JavaFX Beans property with the ObjectProperty as the root, and the successive JavaFX
Beans property names that reach into the deeply nested JavaFX Beans property as the rest of the arguments.

■■Note There is another set of select methods that takes an ObservableValue as the first parameter. They
were introduced in JavaFX 2.0. The set of select methods that takes an Object as the first parameter allows us
to use any Java object, not merely JavaFX Beans, as the root of a selection binding.

119

Chapter 3 ■ Properties and Bindings

In the following example, we use a few classes from the javafx.scene.effect package—Lighting and
Light—to illustrate how the selection operator works. We teach you how to apply lighting to a JavaFX scene
graph in a later chapter of the book. For now, our interest lies in the fact that Lighting is a JavaFX bean that
has a property named light whose type is Light, and that Light is also a JavaFX bean that has a property
named color whose type is Color (in javafx.scene.paint).
The program in Listing 3-15 illustrates how to observe the color of the light of the lighting.
Listing 3-15. SelectBindingExample.java
import
import
import
import
import
import
import
import
import

javafx.beans.binding.Bindings;
javafx.beans.binding.ObjectBinding;
javafx.beans.property.ObjectProperty;
javafx.beans.property.SimpleObjectProperty;
javafx.beans.value.ChangeListener;
javafx.beans.value.ObservableValue;
javafx.scene.effect.Light;
javafx.scene.effect.Lighting;
javafx.scene.paint.Color;

public class SelectBindingExample {
    public static void main(String[] args) {
        ObjectProperty root = new SimpleObjectProperty<>(new Lighting());
        final ObjectBinding selectBinding = Bindings.select(root, "light", "color");
        selectBinding.addListener(new ChangeListener() {
            @Override
            public void changed(ObservableValue observableValue, Color
                oldValue, Color newValue) {
                System.out.println("\tThe color changed:\n\t\told color = " +
                    oldValue + ",\n\t\tnew color = " + newValue);
            }
        });
        System.out.println("firstLight is black.");
        Light firstLight = new Light.Point();
        firstLight.setColor(Color.BLACK);
        System.out.println("secondLight is white.");
        Light secondLight = new Light.Point();
        secondLight.setColor(Color.WHITE);
        System.out.println("firstLighting has firstLight.");
        Lighting firstLighting = new Lighting();
        firstLighting.setLight(firstLight);
        System.out.println("secondLighting has secondLight.");
        Lighting secondLighting = new Lighting();
        secondLighting.setLight(secondLight);
        System.out.println("Making root observe firstLighting.");
        root.set(firstLighting);

120

Chapter 3 ■ Properties and Bindings

        System.out.println("Making root observe secondLighting.");
        root.set(secondLighting);
        System.out.println("Changing secondLighting's light to firstLight");
        secondLighting.setLight(firstLight);
        System.out.println("Changing firstLight's color to red");
        firstLight.setColor(Color.RED);
    }
}
In this example, the root is an ObjectProperty that observes Lighting objects. The binding
colorBinding observes the color property of the light property of the Lighting object that is the value of
root. We then created some Light and Lighting objects and changed their configuration in various ways.
When we run the program in Listing 3-15, the following output is printed to the console:
firstLight is black.
secondLight is white.
firstLighting has firstLight.
secondLighting has secondLight.
Making root observe firstLighting.
    The color changed:
        old color = 0xffffffff,
        new color = 0x000000ff
Making root observe secondLighting.
    The color changed:
        old color = 0x000000ff,
        new color = 0xffffffff
Changing secondLighting's light to firstLight
    The color changed:
        old color = 0xffffffff,
        new color = 0x000000ff
Changing firstLight's color to red
    The color changed:
        old color = 0x000000ff,
        new color = 0xff0000ff
As expected, a change event is fired for every change in the configuration of the object being observed
by root, and the value of colorBinding always reflects the color of the light of the current Lighting object in
root.

■■Caution The JavaFX properties and bindings framework does not issue any warnings if the supplied
property names do not match any property names in a JavaFX bean. It will simply have the default value for the
type: null for object type, zero for numeric types, false for boolean type, and the empty string for string type.

121

Chapter 3 ■ Properties and Bindings

Adapting JavaBeans Properties to JavaFX Properties
Over the many years since the JavaBeans specification was published, a lot of JavaBeans were written for
various projects, products, and libraries. To better help Java developers leverage these JavaBeans, a set of
adapters were provided in the javafx.beans.properties.adapter package to make them useful in the
JavaFX world by creating a JavaFX property out of JavaBeans properties.
In this section, we first briefly review the JavaBeans specification definition of properties, bound
properties, and constrained properties by way of a simple example. We then show you how to create JavaFX
properties out of JavaBeans properties using the adapters.

Understanding JavaBeans Properties
JavaBeans properties are defined using the familiar getter and setter naming convention. A property is “read
only” if only a getter is provided, and it is “read/write” if both a getter and a setter are provided. A JavaBeans
event is made up of the event object, the event listener interface, and listener registration methods on the
JavaBean. Two particular kinds of events are available for use by JavaBeans properties: A PropertyChange
event can be fired when a JavaBeans property is changed; a VetoableChange event can also be fired when
a JavaBeans property is changed; and if the listener throws a PropertyVetoException, the property change
should not take effect. A property whose setter fires PropertyChange events is called a bound property.
A property whose setter fires VetoableChange events is called a constrained property. Helper classes
PropertyChangeSupport and VetoableChangeSupport allow bound properties and constrained properties to
be easily defined in JavaBean classes.
Listing 3-16 defines a JavaBean Person with three properties: name, address, and phoneNumber. The
address property is a bound property, and the phoneNumber property is a constrained property.
Listing 3-16. Person.java
import
import
import
import
import

java.beans.PropertyChangeListener;
java.beans.PropertyChangeSupport;
java.beans.PropertyVetoException;
java.beans.VetoableChangeListener;
java.beans.VetoableChangeSupport;

public class Person {
    private PropertyChangeSupport propertyChangeSupport;
    private VetoableChangeSupport vetoableChangeSupport;
    private String name;
    private String address;
    private String phoneNumber;
    public Person() {
        propertyChangeSupport = new PropertyChangeSupport(this);
        vetoableChangeSupport = new VetoableChangeSupport(this);
    }
    public String getName() {
        return name;
    }

122

Chapter 3 ■ Properties and Bindings

    public void setName(String name) {
        this.name = name;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        String oldAddress = this.address;
        this.address = address;
        propertyChangeSupport.firePropertyChange("address", oldAddress, this.address);
    }
    public String getPhoneNumber() {
        return phoneNumber;
    }
    public void setPhoneNumber(String phoneNumber) throws PropertyVetoException {
        String oldPhoneNumber = this.phoneNumber;
        vetoableChangeSupport.fireVetoableChange("phoneNumber", oldPhoneNumber,
phoneNumber);
        this.phoneNumber = phoneNumber;
        propertyChangeSupport.firePropertyChange("phoneNumber", oldPhoneNumber, this.
phoneNumber);
    }
    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }
    public PropertyChangeListener[] getPropertyChangeListeners() {
        return propertyChangeSupport.getPropertyChangeListeners();
    }
    public void addVetoableChangeListener(VetoableChangeListener l) {
        vetoableChangeSupport.addVetoableChangeListener(l);
    }
    public void removeVetoableChangeListener(VetoableChangeListener l) {
        vetoableChangeSupport.removeVetoableChangeListener(l);
    }
    public VetoableChangeListener[] getVetoableChangeListeners() {
        return vetoableChangeSupport.getVetoableChangeListeners();
    }
}

123

Chapter 3 ■ Properties and Bindings

Understanding the JavaFX Property Adapters
The interfaces and classes in the javafx.beans.property.adapter package can be used to easily adapt
JavaBeans properties to JavaFX properties. The ReadOnlyJavaBeanProperty interface is a subinterface of
ReadOnlyProperty, and adds two methods:
void dispose()
void fireValueChangedEvent()
The JavaBeanProperty interface extends the ReadOnlyJavaBeanProperty and the Property
interfaces. Each of these two interfaces has concrete class specializations for Boolean, Integer, Long,
Float, Double, Object, and String types. These classes do not have public constructors. Instead,
builder classes are provided to create instances of these types. We use the JavaBeanStringProperty
class in the following example code. The same pattern applies to all other JavaFX property adapters. The
JavaBeanStringPropertyBuilder supports the following methods:
public
public
public
public
public
public
public
public
public

static JavaBeanStringPropertyBuilder create()
JavaBeanStringProperty build()
JavaBeanStringPropertyBuilder name(java.lang.String)
JavaBeanStringPropertyBuilder bean(java.lang.Object)
JavaBeanStringPropertyBuilder beanClass(java.lang.Class)
JavaBeanStringPropertyBuilder getter(java.lang.String)
JavaBeanStringPropertyBuilder setter(java.lang.String)
JavaBeanStringPropertyBuilder getter(java.lang.reflect.Method)
JavaBeanStringPropertyBuilder setter(java.lang.reflect.Method)

To use the builder, start by calling its static method create(). Then call a chain of the methods that
returns the builder itself. Finally, the build() method is called to create the property. For most cases, it
suffices to call the bean() and the name() methods to specify the JavaBean instance and the name of the
property. The getter() and setter() methods can be used to specify a getter and setter that does not follow
the naming convention. The beanClass() method can be used to specify the JavaBean class. Setting the
JavaBean class up front on the builder allows you to more efficiently create adapters for the same JavaBeans
property for multiple instances of the same JavaBean class.

■■Note Although the builders of the JavaFX scene, control, and so on, classes have been deprecated, the
builders in the javafx.beans.property.adapter package have not been deprecated. They are required to
generate the JavaBeans property adapters.
The program in Listing 3-17 illustrates the adaption of the three JavaBeans properties of the Person
class into JavaBeanStringProperty objects.
Listing 3-17. JavaBeanPropertiesExamples.java
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import java.beans.PropertyVetoException;

124

Chapter 3 ■ Properties and Bindings

public class JavaBeanPropertiesExample {
    public static void main(String[] args) throws NoSuchMethodException {
        adaptJavaBeansProperty();
        adaptBoundProperty();
        adaptConstrainedProperty();
    }
    private static void adaptJavaBeansProperty() throws NoSuchMethodException {
        Person person = new Person();
        JavaBeanStringProperty nameProperty = JavaBeanStringPropertyBuilder.create()
            .bean(person)
            .name("name")
            .build();
        nameProperty.addListener((observable, oldValue, newValue) -> {
            System.out.println("JavaFX property " + observable + " changed:");
            System.out.println("\toldValue = " + oldValue + ", newValue = " + newValue);
        });
        System.out.println("Setting name on the JavaBeans property");
        person.setName("Stephen Chin");
        System.out.println("Calling fireValueChange");
        nameProperty.fireValueChangedEvent();
        System.out.println("nameProperty.get() = " + nameProperty.get());
        System.out.println("Setting value on the JavaFX property");
        nameProperty.set("Johan Vos");
        System.out.println("person.getName() = " + person.getName());
    }
    private static void adaptBoundProperty() throws NoSuchMethodException {
        System.out.println();
        Person person = new Person();
        JavaBeanStringProperty addressProperty = JavaBeanStringPropertyBuilder.create()
            .bean(person)
            .name("address")
            .build();
        addressProperty.addListener((observable, oldValue, newValue) -> {
            System.out.println("JavaFX property " + observable + " changed:");
            System.out.println("\toldValue = " + oldValue + ", newValue = " + newValue);
        });
        System.out.println("Setting address on the JavaBeans property");
        person.setAddress("12345 Main Street");
    }
    private static void adaptConstrainedProperty() throws NoSuchMethodException {
        System.out.println();
        Person person = new Person();
        JavaBeanStringProperty phoneNumberProperty = JavaBeanStringPropertyBuilder.create()
            .bean(person)
            .name("phoneNumber")
            .build();

125

Chapter 3 ■ Properties and Bindings

        phoneNumberProperty.addListener((observable, oldValue, newValue) -> {
            System.out.println("JavaFX property " + observable + " changed:");
            System.out.println("\toldValue = " + oldValue + ", newValue = " + newValue);
        });
        System.out.println("Setting phoneNumber on the JavaBeans property");
        try {
            person.setPhoneNumber("800-555-1212");
        } catch (PropertyVetoException e) {
            System.out.println("A JavaBeans property change is vetoed.");
        }
        System.out.println("Bind phoneNumberProperty to another property");
        SimpleStringProperty stringProperty = new SimpleStringProperty("866-555-1212");
        phoneNumberProperty.bind(stringProperty);
        System.out.println("Setting phoneNumber on the JavaBeans property");
        try {
            person.setPhoneNumber("888-555-1212");
        } catch (PropertyVetoException e) {
            System.out.println("A JavaBeans property change is vetoed.");
        }
        System.out.println("person.getPhoneNumber() = " + person.getPhoneNumber());
    }
}
In the adaptJavaBeanProperty() method, we instantiated a Person bean and adapted its name
JavaBeans property into a JavaFX JavaBeanStringProperty. To help you understand when a ChangeEvent
is delivered to the nameProperty, we added a ChangeListener (in the form of a lambda expression) to it.
Because name is not a bound property, when we call person.setName(), the nameProperty is not aware of
the change. To notify nameProperty of the change, we call its fireValueChangedEvent() method. When we
call nameProperty.get(), we get the name that we have set on the person bean. Conversely, after we call
nameProperty.set(), a call to person.getName() will return what we have set on nameProperty.
In the adaptBoundProperty() method, we instantiated a Person bean and adapted its address
JavaBeans property into a JavaFX JavaBeanStringProperty. To help you understand when a ChangeEvent
is delivered to the addressProperty, we added a ChangeListener (in the form of a lambda expression) to
it. Because address is a bound property, the addressProperty is registered as a PropertyChangeListener
to the person bean; therefore, when we call person.setAddress(), the addressProperty is notified
immediately without us having to call the fireValuechangedEvent() method.
In the adaptConstrainedProperty() method, we instantiated a Person bean and adapted its
phoneNumber JavaBeans property into a JavaBeanStringProperty. Again we added a ChangeListener
to it. Because phoneNumber is a constrained property, phoneNumberProperty is capable of vetoing
person.setPhoneNumber() calls. When that happens, the person.setPhoneNumber() call throws a
PropertyVetoException. The phoneNumberProperty will veto such a change if it is itself bound to another
JavaFX property. We call person.setPhoneNumber() twice, once before we bind phoneNumberProperty to
another JavaFX property, and once after phoneNumberProperty is bound. The first call succeeds in altering
the value of the phoneNumberProperty, and the second call throws a PropertyVetoException.

126

Chapter 3 ■ Properties and Bindings

When we run the program in Listing 3-17, the following output is printed to the console:
Setting name on the JavaBeans property
Calling fireValueChange
JavaFX property StringProperty [bean: Person@776ec8df, name: name, value: Stephen Chin] changed:
        oldValue = null, newValue = Stephen Chin
nameProperty.get() = Stephen Chin
Setting value on the JavaFX property
JavaFX property StringProperty [bean: Person@776ec8df, name: name, value: Johan Vos] changed:
        oldValue = Stephen Chin, newValue = Johan Vos
person.getName() = Johan Vos
Setting address on the JavaBeans property
JavaFX property StringProperty [bean: Person@41629346, name: address, value: 12345 main
Street] changed:
        oldValue = null, newValue = 12345 main Street
Setting phoneNumber on the JavaBeans property
JavaFX property StringProperty [bean: Person@6d311334, name: phoneNumber, value: 800-5551212] changed:
        oldValue = null, newValue = 800-555-1212
Bind phoneNumberProperty to another property
JavaFX property StringProperty [bean: Person@6d311334, name: phoneNumber, value: 866-5551212] changed:
        oldValue = 800-555-1212, newValue = 866-555-1212
Setting phoneNumber on the JavaBeans property
A JavaBeans property change is vetoed.
person.getPhoneNumber() = 866-555-1212

Summary
In this chapter, you learned the fundamentals of the JavaFX properties and bindings framework, and the
JavaFX Beans specification. You should now understand the following important principles.
•

JavaFX properties and bindings are the fundamental workhorses of the framework.

•

They conform to the key interfaces of the framework.

•

They fire two kinds of events: an invalidation event and a change event.

•

All properties and bindings provided by the framework recalculate their values
lazily—only when a value is requested. To force them into eager reevaluation, a
ChangeListener needs to be attached.

•

New bindings are created out of existing properties and bindings in one of three
ways: using the factory methods of the Bindings utility class, using the fluent
interface API, or directly extending the IntegerBinding series of abstract classes.

•

The JavaFX Beans specification uses three methods to define a property: the getter,
the setter, and the property getter.

127

Chapter 3 ■ Properties and Bindings

•

JavaFX Beans properties can be implemented through the eager, half-lazy, and
full-lazy strategies.

•

Old-style JavaBeans properties can be adapted easily to JavaFX properties.

Resources
The following are useful resources on properties and bindings.

128

•

Martin Fowler’s write-up on fluent interface APIs: www.martinfowler.com/bliki/
FluentInterface.html

•

The Properties and Binding tutorial at Oracle’s JavaFX.com: http://docs.oracle.
com/javase/8/javafx/properties-binding-tutorial/

•

Michael Heinrichs’s blog includes entries on JavaFX properties and bindings:
http://blog.netopyr.com/

CHAPTER 4

Using Scene Builder to Create a
User Interface
Give me a lever long enough and a fulcrum on which to place it, and I shall move the
world.
—Archimedes
In Chapter 2, you learned about the two ways of creating a JavaFX UI, programmatically and declaratively,
and how to programmatically create a UI using the JavaFX APIs. You are familiar with the theater metaphor
of the JavaFX UI, with the Stage representing a window in a Windows, Mac, or Linux program, or the touch
screen in a mobile device, the Scene and the Nodes it contains representing the content of the UI. In this
chapter, we tackle the other side of the UI story in JavaFX: the declarative creation of UIs.
At the center of this approach of UI design is the FXML file. It is an XML file format designed specifically
to hold information about UI elements. It contains the “what” of the UI elements, but not the “how.” This is
why this method of creating JavaFX UIs is called declarative. At its core, FXML is a Java object serialization
format that can be used for any Java classes written in a certain way, including all old-style JavaBeans. In
practice, however, it is only used for specifying JavaFX UIs.
Aside from direct editing in a text editor or your favorite Java integrated development environment
(IDE), FXML files can also be manipulated by a graphical tool designed for working with FXML files called
JavaFX Scene Builder. JavaFX Scene Builder 1.0 was released in August 2012, and JavaFX Scene Builder 1.1
was released in September 2013. Both 1.0 and 1.1 work with JavaFX 2. JavaFX Scene Builder 2.0 was released
in May 2014 and works with JavaFX 8. The JavaFX Scene Builder 2.0 code base is released as open source,
and while the Oracle JavaFX team is still contributing to it, the development and release of Scene Builder is
now coordinated by Gluon.
Gluon merges contributions from Oracle, Gluon engineers and community contributors, and maintains
a public code repository and an issue tracker. Also, Gluon creates binary releases for Windows, Mac, and
Linux. All information on Scene Builder, including how to download and install, is now maintained at
http://gluonhq.com/products/scene-builder/.
JavaFX Scene Builder is a fully graphical tool that allows you to paint the screens of your UI out of a
palette of available containers, controls, and other visual nodes, and lay them out by direct manipulation on
the screen and modification of their properties through property editors.
FXML files are loaded into JavaFX applications by the JavaFX runtime using the FXMLLoader class. The
result of loading an FXML file is always a Java object, usually a container Node such as a Group or a Pane.
This object can be used as the root in a Scene, or attached to a bigger programmatically created scene graph
as a node. To the rest of the JavaFX application, the nodes loaded from an FXML file are no different from
programmatically constructed nodes.
© Johan Vos, Stephen Chin, Weiqi Gao, James Weaver, and Dean Iverson 2018
J. Vos et al., Pro JavaFX 9, https://doi.org/10.1007/978-1-4842-3042-8_4

129

Chapter 4 ■ Using Scene Builder to Create a User Interface

We present the intricately related materials about the content and format of FXML files, how they
are loaded at runtime, and how they are fashioned at design time, in a spiraling progression. We start this
chapter with a complete example showing how the StageCoach program in Listing 2-1 from Chapter 2 can
be done using FXML. We then present the FXML loading facility in detail. We then present a series of small
handcrafted FXML files that highlight all the features of the FXML file format. Once you understand the
FXML file format, we show you how to create these FXML files using JavaFX Scene Builder, covering all the
features of JavaFX Scene Builder 2.0.

■■Note

You will need to download and install Gluon’s open source JavaFX Scene Builder 9.0 from
http://gluonhq.com/products/scene-builder/ to go through the examples in this chapter. We also highly
recommend configuring your favorite IDE to use JavaFX Scene Builder 9.0 to edit FXML files. NetBeans and
IntelliJ IDEA come bundled with JavaFX support. Eclipse users can install the e(fx)clipse plug-in. Once configured,
you can right-click any FXML files in your projects in your IDE and select the “Edit with Scene Builder” context
menu item. Of course, you can also use your IDE’s XML file editing capabilities to edit FXML files as XML files.

Setting the Stage with FXML
The process of converting the StageCoach program in Chapter 2 from using a programmatically created UI
to using a declaratively created UI is straightforward.

Creating a User Interface Graphically with JavaFX Scene Builder
We first created an FXML file that represented the root node of the scene with JavaFX Scene Builder.
Figure 4-1 shows a screenshot as this UI is being created.

Figure 4-1. StageCoach.fxml being created in JavaFX Scene Builder

130

Chapter 4 ■ Using Scene Builder to Create a User Interface

We will go into details of how to use JavaFX Scene Builder in the latter half of this chapter. For now,
simply observe the major functional areas of the tool. In the middle is the Content panel showing the look
of the UI being worked on. On the left side are the Library panel at the top, which includes all the possible
nodes that can be used in the Content panel divided into neat subsets such as Containers, Controls, Shapes,
Charts, and so on, and the Document panel below, which shows the scene graph that is being worked on in
the Content panel as a tree structure called the Hierarchy, and the Controller that provides event handler
code for the various controls in the UI. On the right side is the Inspector area that has subareas that allow
you to manipulate Properties, Layout, and Code hookup of the currently selected control.

Understanding the FXML File
Listing 4-1 shows the FXML file that is saved by JavaFX Scene Builder from the UI we have created.
Listing 4-1. StageCoach.fxml