javafx.scene.Node
. When using these nodes, you create a tree structure of components; for instance, nodes can hold other nodes that are children, as needed. A node that has children is a parent node. The javafx.scene.Parent
class inherits from the Node
class and allows you to manage the child nodes. The VBox
and HBox
classes used in the example inherit from the Parent
class, and the hierarchy of the application is created by using these two classes and the Button
control. As with the HBox
and VBox
classes, every control is a node. You may notice that, like the Component
class in Swing, the inheritance hierarchy of the Node
class is a bit more complicated. This will be described in more detail later in this chapter (see the “Node Types” section).HBox
and VBox
classes manage the layout of child nodes by considering the preferred bounds of the child nodes. To display a component, you just need to add it to a visible scene graph. Each scene must have a root node, which is the main node of the scene graph and which contains all other nodes. Therefore, you need a Parent
type. As you saw in the previous chapter’s example, you can add a scene graph to a stage. By showing the stage on the screen when launching the application, the scene graph and all its visible nodes will be rendered on the surface. Figure 3-2 outlines a simplified structure of a JavaFX application.Scene
class that defines the scene graph holds the root node and its children. The Scene
class also provides a number of features and an inheritance hierarchy. Before looking at those topics, let’s first review the rest of the basic properties and functions of the Scene
class. Table 3-1 provides an overview of the most important properties of the Scene
class.NOTE
You can access most properties either by using the JavaFX property API or (the POJO way) by using getter and setter methods. Table 3-1 and all following tables in this book will cover the JavaFX properties. Sometimes, only the properties that are specific to the current topic or that are used in common use cases will be shown in these tables. For additional information, refer to the JavaDoc or the JavaFX source code. |
Scene
class. The code shows two different ways to set a property: by calling the appropriate setter method, which is setFill()
in this case, and by retrieving the property object using the appropriate method. The method mySceneGraph.nodeOrientationProperty()
is used in this case, and it returns the property instance of type ObjectProperty<NodeOrientation>
. The value of the property can be set by calling setValue(…)
. When looking at the source code of the Scene
class, you can see that the setter method does the same thing. These properties provide better styling and give the application its first functionalities.Scene
class. A different background color and a mouse cursor are set for the scene graph, and the nodes’ orientation is changed. To see the background color of the scene graph, the background of the HBox
and VBox
is set to null since these nodes have their own background color by default and their layout is always maximized in the scene graph. Button 1 is now on the right side of the window instead of the left side, and so on.VBox
is no longer part of the hierarchy and is therefore not displayed. However, two of the EventHandler
properties have been set. The event handlers that are defined here will always execute when a key or a mouse button is pressed in the scene graph; when that happens, the root node of the scene graph will change. You can see this behavior in action by clicking the black background of the demo application. Now Button 4 and Button 5 should appear. By pressing any key on the keyboard, the HBox
with the corresponding button will appear again.NOTE
Lambda expressions are used to define the EventHandler properties in the source code of the demo application. Lambdas are a new language feature introduced in version 8 of Java, so some developers may not have used them yet. JavaFX is well designed for the use of lambda expressions, which are simply source code. For this reason, I use them in this book as often as possible. In theory, all examples shown in the book can be refactored to a version that doesn’t use lambda expressions. However, refactoring the previous example makes the two lines of code increase to 12 lines (as shown in the following listing) by using internal classes. |
snapshot(…)
method to create a screenshot of the current scene graph. In the example, this method is called inside the saveSnapshotOnDisc(…)
method. The snapshot is created and saved as a PNG file on the hard drive. By pressing any key, the KeyEventHandler
creates the snapshot.javafx.scene.input.InputEvent
. JavaFX can deal with considerably more input types than Swing. Additionally, all modern input types such as gestures and touch input are supported. By using these features, developers can define a better user experience than with any other Java UI framework. Figure 3-3 contains an overview of the supported event types and their inheritance.NOTE
All modern MacBooks embed a multitouchpad that can interpret gestures. Most of them are supported in the Mac OS, and if a JavaFX program is running on such a system, the gesture events will be passed to the program. If you don’t have a tablet but own a MacBook, you can easily test the gesture events by using the touchpad of your device. |
EventHandler
can be set to react after a special button is clicked:mouseTransparent
property to true. The target of a key event is the currently focused node. Touch and gesture events have some special rules for how the target node will be selected too. After the target node is defined, a route through the scene graph will be defined. This route contains all nodes between the root node of the scene graph and the target node. This route is called the event dispatch chain. In the event-capturing phase, the event will be passed through the chain. This dispatch starts with the scene graph instance and goes down the complete route to the target node. If any of these nodes has registered an event filter for the given event type, the event handler that is defined as the filter will be called. Each event has a consumed flag. Whenever any event handler consumes an event by setting this flag, the processing of the event will stop. If no registered filter consumes the event, the target node will be reached. At this moment, the last phase will start. In the event-bubbling phase, the event will return along the dispatch chain from the target to the root node of the scene graph. Here, each event handler that is registered for the given event type will be called. As long as no handler consumes the event, it will return to the next node up the chain until the root node is reached, and the event dispatching will stop.Node
class. The inheritance of the Node
class is a little more complicated than you might know from other cases such as Swing, for example. Figure 3-5 shows a diagram with the most important parts of the class hierarchy.Node
class can be added in this way in the scene graph.RadioButton
control is internally composed from different nodes. To display the text information on the radio button, a LabeledText
object is used. This is a derivative of the javafx.scene.shape.Text
class and mainly used in the skin of controls. The selection circle of the radio button is created by two nested StackPanes. These StackPanes are styled by CSS to get the specific look shown here. The individual nodes will be styled and laid out by the radio button skin. This special functionality will be explained in Chapters 9 and 10. In Swing, an inner canvas is used in which pixel graphics are painted to define the visual representation of a component. You won’t find this technology in JavaFX nodes.NOTE
JavaFX contains a special node type called Canvas . This node provides paint functionality like you may know from Swing and Java2D, but the Canvas class is never used internally in JavaFX nodes. Here, CSS and other node types such as shapes are always used. The Canvas class will be described in Chapter 7. |
javafx.scene.Parent
class. This class provides only the ability to wrap any type of node. As a next step, the javafx.scene.layout.Region
class defines the Region
type that is a resizable Parent
class that can be styled using CSS. The Parent
and Region
classes can both contain a set of children. The children of a parent are defined as an ObservableList<Node>
. Neither the Parent
nor Region
class provide public methods to change its children. The javafx.scene.layout.Pane
class adds this feature. While the Parent
class defines the getChildrenUnmodifiable()
method that returns a read-only list of all children, the Pane
class defines a getChildren()
method that returns a modifiable list of children. Therefore, all default layout containers in JavaFX, such as HBox
or VBox
, extend the Pane
class, and developers can add child nodes to them by calling pane.getChildren().add(myButton)
, for example. All controls such as Button
controls inherit from the Region
class because they need to hold and lay out some primitive shapes that are needed to visualize the button. But developers can never easily add new nodes to a button. By using the descried class hierarchy, it will be more difficult for developers to use some of the node types in a wrong way.Pane
class. Instances of these classes should be used when all the controls inside a scene graph need to be laid out. In the previous examples, you already saw two pane types: HBox
and VBox
. Table 3-3 describes the various layout panes.HBox
or FlowPane
, can be easily used in code. Others, such as the AnchorPane
, are flexible, and therefore they need more configuration in code to create a special layout. As a best practice, you should create these layouts in Scene Builder. When using Scene Builder, you create an FXML file that describes the layout. (FXML will be discussed in the “FXML” section, and an example with a complex layout that is created in Scene Builder will be shown.) To achieve the desired layout structure, you can nest different containers within a JavaFX application. The first demo application in this chapter wrapped a VBox
inside an HBox
to create a special screen layout, for example. As you will see later, it is easy to create your own LayoutPanes, too, but doing so requires knowledge about the internal layout mechanism of JavaFX.Node
class are components that fall into the complex node category. The largest part of this group includes the controls such as buttons, labels, and tables. These all extend the Control
class. Controls have a lot of features; you can simply change their style and behavior or add a tooltip, for example. I will discuss the different features of controls in more depth in the following chapters. In addition to them, there are some other special nodes such as WebView
, Canvas
, and Chart
. Table 3-4 describes these components.Node
class provides some helpful methods that can be used to manage some basic tasks when working with nodes. Some of these methods are related to the ones that are part of the Scene
class. All the different methods of the Node
class won’t be shown here. For a complete overview, please refer to the JavaDoc or the source of the Node class.changeNodeVisibility(…)
method is called. This method uses the lookup(…)
method to find the node with the given ID inside the scene graph. Because the lookup(…)
method uses CSS ID specification, a hash tag must be added as a prefix to the ID. (You can read more about the specification at www.w3.org/TR/CSS21/syndata.html#value-def-identifier.) Clicking the button changes the visibility of the check box.NOTE
This example application has some new features that weren’t mentioned until now. By calling the setOnAction(…) method on the Button instance, a special event handler is set on the button. This handler will be called whenever the button is clicked or, rather, used. This can be triggered by different input events, such as a mouse click, a pressed key, or a touch event, for example. Most of the controls that are provided by JavaFX support event handlers for common use cases. A ComboBox supports event handler instances to react to the behavior of the selection pop-up, for example. |
AnchorPane
, which is considerably more complex than the BorderPane
or FlowPane
. In the following code block, an AnchorPane
is used to lay out two buttons. The AnchorPane
class offers some static methods to affect the layout behavior of child components in an AnchorPane
. Figure 3-9 shows the application view.AnchorPane
with the layout that was developed in Java source earlier:FXMLLoader
class:getClass().getResource(…)
is used to load the FXML file. This is a best practice to load resources in Java. To do this, you need to place the layout.fxml
file in the same package as the class file, SceneGraphApp.class
in this case. Incidentally, most modern Java applications use a setup that was defined by Maven some years ago where all Java code is placed in an src/main/java
subfolder. It is recommend that you use this structure and don’t put any resource files such as images, XML, or CSS files in this folder. For those resources, create an src/main/resources
folder. The getResource(…)
method will still work if the resource is located in the same package as the class but in a resources folder. Here is a working folder tree for the given example:src/main/java
and src/main/resources
automatically to the classpath. Sometimes, you need to set up your environment by hand and add these folders to the classpath.AnchorPane
, new parameters are defined for the buttons in the FXML. The fx:id
parameters define unique IDs for the two buttons. By using these IDs, the buttons can be accessed in the controller class. This can be done by using special annotations. Event handlers can be defined in FXML too. In the definition of the save button, for example, an additional parameter is added. This onAction
parameter links to a method that is part of the controller. When the button is clicked, the save method will be triggered. Furthermore, each controller class can define an initialize()
method. This method will be called when the controller is created and all annotated values are injected. The following code shows how a controller that matches the FXML file may look:@FXML
annotation will be filled by injection. Additionally, the save(…)
method will be bound to the onAction
event of the save button.18.218.151.44