btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
message.setText("Hello World! JavaFX style :)");
}
});
avaFX 8 Essentials and Creating a Custom UI
Getting to know the essentials of JavaFX will definitely help you to easily build complicated and complex UI solutions.
In this chapter, you will get a brief introduction about JavaFX 8 architecture, so you get an idea of how JavaFX architecture components and engines interconnect together with your JavaFX application efficiently and render its graphics smoothly.
You will learn how to render graphics on the JavaFX scene and, for that, we will create a basic application using a scene, some controls, and styling.
We will touch upon the fundamentals of Java SE 8 features (such as Lambda and functional interfaces) to help increase code readability, quality, and productivity.
Once we have our first structured JavaFX 8 application, wouldn’t it be nice if you could change the UI of your application without altering its functionality? You will learn about theming by having a glance at the fundamentals of JavaFX CSS styling.
Finally, you will find out how to use Scene Builder to create and define UI screens graphically and save them as a JavaFX FXML-formatted file. And you will get hands-on experience of creating custom controls.
In this chapter, we’ll cover the following topics:
-
Understanding JavaFX architecture components
-
Using JavaFX components to set up the UI
-
Using Java SE 8, Lambda expressions, and other features
-
Theming your application to target different platforms
-
Customizing the application UI with CSS
-
Using the Scene Builder tool to create the UI visually
-
Building a custom UI with FXML
Quick review of the JavaFX 8 architecture
To better understand how the framework’s components and engines interact together to run your JavaFX application, this section gives a high-level description of the JavaFX architecture and ecosystem.
The following figure illustrates the JavaFX platform’s architectural components. It displays each component and how each of them interconnects.
The engine that is responsible for running your JavaFX application code lies below the JavaFX public APIs.
This engine is composed of subcomponents. These include Prism, a JavaFX high-performance graphics engine; the Glass toolkit, a small and efficient windowing system; a media engine; and a web engine.
Scene graphs
Every application has a starting root point to construct a UI hierarchy, and the starting point for JavaFX applications is the scene graph. In the preceding screenshot, it is shown as part of the top layer in blue. It is the root tree of nodes that represents all of the visual elements of the application’s user interface. It also tracks and handles any user input and can be rendered, as it is itself a UI node.
Node is any single element in the scene graph tree. Each node has these properties by default – an ID for identification, a list of style classes for changing its visual properties, and a bounding volume to fit correctly on the scene with other components and inside its parent layout container node, with the exception of the root node of a scene graph.
Each node in a scene graph tree has a single parent but could have zero or more children; however, the scene root has no parent (is null). Moreover, JavaFX has a mechanism to ensure a node could have only a single parent; it can also have the following:
-
Visual effects, such as blurs and shadows
-
Controlling components transparency via opacity
-
CPU-accelerated 2D transforms, transitions, and rotations
-
3D transforms such as transitions, scaling, and rotations
-
Event handlers (such as mouse events, key events, or other input methods such as touch events)
-
An application-specific state
The following figure shows the relationship between the stage, scene, UI nodes, and graphical tree:
The graphics primitives also are an integral part of the JavaFX scene graph, such as lines, rectangles, and text in addition to images, media, UI controls, and layout containers.
When it comes to delivering complex and rich UIs for customers, scene graphs simplify this task. Also, you can use the javafx.animation APIs to quickly and easily animate various graphics in the scene graph.
In addition to these features, the javafx.scene API allows the creation and specification of several content types as the following:
-
노드: Any node element represented as UI controls, charts, groups, containers, embedded web browser, shapes (2-D and 3-D), images, media, and text
-
효과: change its appearance on the scene graph node, such as blurs, shadows, and color adjustment
-
상태: Any application-specific state such as transforms (positioning and orientation of nodes) and visual effects
Java public APIs for JavaFX features
This is your Swiss-knife toolkit provided as a complete set of APIs that support rich client application development.
These APIs provide you with unprecedented flexibility to construct your rich client UI applications by combining the best capabilities of the Java SE platform with comprehensive, immersive media functionality into an intuitive and comprehensive one-stop development environment at hand.
These Java APIs for JavaFX allow you to do the following:
-
Use the powerful features of Java SE, from generics, annotations, and multithreading, to new Lambda Expressions (introduced in Java SE 8).
-
Provides an easier way for web developers to use JavaFX from other JVM-based dynamic languages, such as JavaScript. Writing large and complex JavaFX applications by integrating other system languages, such as Groovy.
-
Binding your UI controls to your controller attributes for automatic notifications and updates to be reflected from your model to bound UI nodes. Binding includes support for high-performance lazy binding, binding expressions, bound sequence expressions, and partial bind reevaluation. We will see this in action and more in Chapter 3, Developing a JavaFX Desktop and Web Application.
-
Introducing observable lists and maps, which allow applications to wire UIs to data models to observe the changes in those data models and update the corresponding UI control accordingly by extending the Java collections library.
Graphics System
The JavaFX Graphics System, shown in Purple in the preceding figure, supports both 2D and 3D scene graphs to run smoothly on the JavaFX scene graph layer. As it is the implementation detail beneath this layer, it provides the rendering software stack when running on a system that doesn’t have sufficient graphics hardware to support hardware-accelerated rendering.
The JavaFX platform has two graphics-accelerated pipelines that implement:
-
Prism: This is the engine that processes all render jobs. It can run on both hardware and software renderers, including 3D. Rasterization and rendering of JavaFX scenes are taken care of by this engine. Based on the device being used, the following multiple render paths are possible:
-
DirectX 9 on Windows XP and Vista, and DirectX 11 on Windows 7
-
OpenGL on Linux, Mac, and Embedded
-
Software rendering when hardware acceleration is not possible
-
-
Quantum Toolkit: This is responsible for linking the Prism engine and Glass Windowing Toolkit together to make them available to the JavaFX layer above them in the stack. This is in addition to managing any threading rules related to rendering versus event handling.
Glass Windowing Toolkit
The Glass Windowing Toolkit, shown in red in the middle portion of the preceding figure, serves as the platform-dependent layer that connects the JavaFX platform to the native operating system.
As its main responsibility is to provide native operating services, such as managing the timers, windows, and surfaces, its position in the rendering stack is the lowest.
JavaFX threads
Normally, the system runs two or more of the following threads at any given time:
-
JavaFX application thread: This is the main and primary thread used by the JavaFX application to run.
-
Prism render thread: This handles rendering separately from the event dispatcher. It renders frame N while frame N +1 is being prepared to process next. Its big advantage is the ability to perform concurrent processing, especially on modern systems that have multiple processors.
-
Media thread: This runs in the background and synchronizes the latest frames through the scene graph by using the JavaFX application thread.
-
Pulse: This enables you to have a way to handle events asynchronously. It helps you manage synchronization between the JavaFX scene graph elements state and an event of the scene graph with the Prism engine. When it is fired, the state of the elements on the scene graph is synchronized to the rendering layer.
The Glass Windowing Toolkit executes all pulse events using high-resolution native timers to make the execution.
Media and images
javafx.scene.media APIs provide media functionality. JavaFX supports both visual and audio media. For audio files, it supports MP3, AIFF, and WAV files and FLV video files.
You can access your media functionalities through three main separate components provided by JavaFX media – the Media object represents a media file, MediaPlayer plays a media file, and MediaView is a node that displays the media into your scene graph.
Note The Media Engine component, shown in orange in the preceding figure, has been designed carefully with stability and performance in mind to provide a consistent behavior across all supported platforms.
Web component
The web engine component, shown in green in the preceding figure, is one of the most important JavaFX UI controls and is built based on the WebKit engine, which is an open source web browser engine that supports HTML5, JavaScript, CSS, DOM rendering, and SVG graphics. It provides a web viewer and full browsing functionality through its API. We will dive deep into this in Chapter 3, Developing a JavaFX Desktop and Web Application, when developing web applications.
It allows you to add and implement the following features in your Java applications:
-
Render any HTML content from a local or remote URL
-
Provide Back and Forward navigation, and support history
-
Reload the content for any updates
-
Animate and apply CSS effects to the web component
-
Provide rich editing control for HTML content
-
Can execute JavaScript commands and handle web control events
Layout components
When building a rich and complex UI, we need a way to allow for flexible and dynamic arrangements of the UI controls within the JavaFX application. This is the best place to use Layout containers or panes.
The Layout API includes the following container classes that automate common layout UI patterns:
-
BorderPane: This lays out its content nodes in the top, bottom, right, left, or center region
-
HBox: This arranges its content nodes horizontally in a single row
-
VBox: This arranges its content nodes vertically in a single column
-
StackPane: This places its content nodes in a back-to-front single stack at the center of the pane
-
GridPane: This enable the creation of a flexible grid of rows and columns in which to lay out content nodes
-
FlowPane: This arranges its content nodes in either a horizontal or vertical flow, wrapping at the specified width (for horizontal) or height (for vertical) boundaries
-
TilePane: This places its content nodes in uniformly sized layout cells or tiles
-
AnchorPane: This enables the creation of anchor nodes to the top, bottom, left-hand side, or center of the layout, and we can freely position its child nodes
JavaFX controls
JavaFX controls are the building blocks of your UI layout, and they reside in the javafx.scene.control package as a set of JavaFX APIs. They are built by using nodes in the scene graph. They can be themed and skinned by JavaFX CSS. They are portable across different platforms. They take full advantage of the visually rich features of the JavaFX platform.
This figure shows some of the UI controls that are currently supported and there are more not shown here as well:
Java SE 8 features
We will scratch the surface of Java SE 8 to understand two of the most important features – lambda or lambda expressions and functional interfaces, which make the lambdas available to us, to help write better, concise, and low-boilerplate JavaFX 8 code. However, keep in mind that we will not address every lambda detail in this book, as this is not a Java SE 8 book.
람다 표현식
The primary goal of project lambda in the Java language is to address the lack of functional programming and provide a way to do functional programming easily by creating anonymous (unnamed) functions in a similar manner to creating anonymous objects instead of methods in Java.
As you saw in the example from Chapter 1, Getting Started with JavaFX 8, we talked about the usual approach for defining a handler for the pressed event on a JavaFX button by using an anonymous inner class:
This code is very verbose as compared to just wiring up a single line of code that sets the text attribute of the message text field in a button action. Wouldn’t it be nice to be able to rewrite this block of code containing your logic without the need of so much boilerplate code?
Java SE 8 solves this with a Lambda expressions as follows:
btn.setOnAction(event -> {
message.setText("Hello World! JavaFX style :)");
});
Syntax
There are two ways to write lambda expressions, and the general form is as shown in the following figure:
Lambda expression general form – creating a new thread as an example
These two ways are as follows:
(param1, param2, ...) -> expression;
(param1, param2, ...) -> { /* code statements */ };
The first form, the expression form, is used when we assign only one line of code or just a simple expression. While the second one, the block form, is a body of single or multiple lines of code, with a return statement, so we need to wrap them with curly braces.
The following three statements are equivalent:
btn.setOnAction((ActionEvent event) -> {message.setText("Hello World!");});
btn.setOnAction( (event) -> message.setText("Hello World!"));
btn.setOnAction(event -> message.setText("Hello World!"));
Tip
To dive deeper into the new lambda expressions and their related features alongside the Java SE 8 features, I encourage you to try this articles series – Java SE 8 new features tour: http://tamanmohamed.blogspot.com/2014/06/java-se-8-new-features-tour-big-change.html
함수적 인터페이스
A lambda expression is fantastic, isn’t it?, but you may be wondering what its exact type is, so it can be assigned to variables and passed to methods.
The answer is in the power of functional interfaces. How?
함수적 인터페이스는 딱 한개의 추상메소드를 갖는 인터페이스를 제공하는 Single Abstract method (SAM)의 개념과 @FunctionalInterface 애노테이션을 사용한다. SAM 패턴이 자바8의 람다 표현식의 핵심이다.
Let’s clear things up with an example to illustrate the concept of both functional interfaces and lambda expressions. I have created a functional interface called Calculator.java containing a single abstract method, calculate(). Once it is created, you can declare and assign variables to lambda expressions. The following is the functional interface:
@FunctionalInterface
public interface Calculator {
double calculate(double width, double height);
}
Now we are ready to create variables and assign them lambda expressions. The following code creates and assigns lambda expressions to our functional interface variables:
Calculator area = (width, height) -> width * height; //Area = w × h
//Perimeter = 2(w+h)
Calculator perimeter = (width, height) -> 2 * (height + width);
out.println("Rectangle area: "+ area.calculate(4, 5)+" cm.");
out.println("Rectangle perimeter: "+ perimeter.calculate(4, 5)+" cm.");
The output of the code should be as follows:
Rectangle area: 20.0 cm.
Rectangle perimeter: 18.0 cm.
Beyond making your code more concise and easy to read, lambda expressions make your code perform better as well.
꾸미기
When working with designers and UX/UI specialists, you will hear about skinning the application or changing its appearance. Both terms are often interchangeable, and both of them reflect the basic concept of theming.
The idea of theming is to change the entire application style by changing its control appearance and without altering its underlying functionality.
In JavaFX, you have the ability to create, modify, or use existing themes to skin your applications, scene, or even just a UI control.
CSS
JavaFX CSS can be applied to any node in the JavaFX scene graph; they are applied to the nodes asynchronously. Styles can also be easily assigned to the scene at runtime, allowing an application’s appearance to change dynamically.
It is based on the W3C CSS version 2.1 specifications, and is currently compatible with some additions from the current work on version 3. The JavaFX CSS support and extensions have been designed to allow JavaFX CSS style sheets to be parsed cleanly by any compliant CSS parser. This enables the mixing of CSS styles for JavaFX and for other purposes (such as for HTML pages) into a single style sheet.
All JavaFX property names are prefixed with an extension of -fx-, including those that might looks compatible with standard HTML CSS, because some JavaFX values have slightly different semantics from standard ones.
Note
For more about JavaFX CSS, see the Skinning JavaFX Applications with CSS document and the reference guide at http://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html.
Applying CSS theme
Here is a custom simple JavaFX CSS rule, ButtonStyle.css, that is to be used in our theming process to theme a button:
/* ButtonStyle.css */
.button {
-fx-text-fill: SKYBLUE;
-fx-border-color: rgba(255, 255, 255, .80);
-fx-border-radius: 8;
-fx-padding: 6 6 6 6;
-fx-font: bold italic 20pt "Arial";
}
We have two ways to apply CSS style sheets to change the look-and-feel theming of our JavaFX applications:
Using a JavaFX Application (javafx.application.Application) class static method setUserAgentStylesheet(String URL) method, which styles all the application hierarchy, including every scene and all child nodes in a JavaFX application. It is used as follows:
Application.setUserAgentStylesheet(getClass().getResource("ButtonStyle.css").toExternalForm());
Now you can use JavaFX 8’s two style sheets that currently come preshipped, Caspian and Modena, and we can switch between them using the same method as shown here:
// Switch to JavaFX 2.x's CASPIAN Look and Feel.
Application.setUserAgentStylesheet(STYLESHEET_CASPIAN);
// Switch to JavaFX 8's Modena Look and Feel.
Application.setUserAgentStylesheet(STYLESHEET_MODENA);
Tip
If you are invoking setUserAgentStylesheet(null) by passing a null value, the default look and feel will be loaded (in this case, Modena) whereas, if you are using JavaFX 2.x Caspian, the default one will be loaded.
Using scene’s getStylesheets().add(String URL) method will style the individual scene and its child node automatically as follows:
Application.setUserAgentStylesheet(null); // defaults to Modena
// apply custom look and feel to the scene.
scene.getStylesheets()
.add(getClass().getResource("ButtonStyle.css")
.toExternalForm());
Basically, the default theme (Modena) will be loaded, as Application.setUserAgentStylesheet(null) is called. It then sets the scene’s additional styling by invoking the getStylesheets().add() method.
Styles are first applied to the parent and then to its children. A node is styled after it is added to the scene graph, regardless of whether it’s shown or not.
The JavaFX CSS implementation applies the following order of precedence – a style from a user agent style sheet has lower priority than a value set from code, which has lower priority than a Scene or Parent style sheet.
Inline styles have the highest precedence. Style sheets from a Parent instance are considered to be more specific than styles from Scene style sheets.