image
CHAPTER
4
Laying Out and Transforming Nodes in the Scene Graph
As shown in the previous chapter, the UI of a JavaFX application is based on a hierarchy of nodes laid out and rendered in the scene graph. By using different interleaved panes, you can create a custom layout. In this chapter, you will take a deeper look at how panes, such as the FlowPane control, work internally. Additionally, you will learn about the different transformation possibilities of JavaFX.
Adding Some Transformations
The scene graph API offers the ability to transform a node or a group of nodes in a user interface. JavaFX supports a set of transformations that can manipulate the position of node pixels in the internal coordination system. For example, scaling a node will not change the defined location of the node, but all the pixels will be transformed to the new position. If the scaling enlarges, node pixels will be added. Internal algorithms are used to calculate the transformed pixels. JavaFX supports five transformations, as listed here:
image  Scaling
image  Rotation
image  Translation
image  Shearing
image  Affine
Figure 4-1 shows samples of the first four transformation types. The Affine transformation is a more general type that can be used to define any transformation between two affine spaces. The Node class has some methods that can be used to define a transformation on it, and you can combine transformations to rotate and scale a node, for example. Table 4-1 contains an overview of the methods that can be used to define transformations on a node.
image
image
image
FIGURE 4-1.Transformation types
image
TABLE 4-1.Overview of Methods of the Node Class to Handle Transformations
The following example, a pane wrapping two buttons, uses gesture events to apply different transformations to an HBox. Because in the scene graph a transformation affects all nodes under the transformed one, the buttons in this application will be transformed as well.
image
image
Once you start the demo, you can transform the objects onscreen by simply using your fingers. Most people will be familiar with this behavior from mobile devices. iOS, for example, heavily uses this same behavior. Figure 4-2 shows an application after a gesture has occurred.
image
image
image
FIGURE 4-2.Transformed nodes
By zooming in on the buttons, you can see that the resolution of the controls won’t become pixelated. All controls are composed of a set of primitives such as shapes and CSS information, and since all these primitives are vector based, a control in JavaFX is scalable without any loss of quality.
Running the Sample Without Gesture Support
The demo should be run on a device that supports gesture events. At the time of writing this book, the Microsoft Surface tablet and MacBook will successfully run the demo. If you don’t have a device that supports gestures and can run JavaFX, simply change the demo to create a transformation experience that can be handled by using only the mouse. To do so, add two sliders to the scene that will change the transformation values. (A slider is a default JavaFX control that will be covered later in the book.) The following code snippet contains a refactored application without gesture events:
image
Once the application is running, you can change the value of the sliders to create the transformations that will affect the location of the HBox and the buttons in the scene graph.
Adding a Third Dimension
The first demo shown earlier in the chapter transforms the components along the x-axis and y-axis of the scene graph. You might be familiar with this behavior from some other toolkits; even with Swing, you can create this effect by using some special Java2D classes. But JavaFX supports the z-axis too. If you have never worked with a 3D system, you can think about this axis as a third dimension that adds depth to the surface. JavaFX’s Shape3D class is the base class for three-dimensional nodes. By using these nodes, you can create a three-dimensional view, as shown in Figure 4-3.
image
image
image
FIGURE 4-3.An application with three-dimensional elements
The 3D support of JavaFX won’t be part of this book. For more information about the different 3D features of JavaFX (2D shapes, lights, textures) you should read the JavaDoc.
Extended Transformation APIs
As you have already seen, it’s easy to define some transformations for a node. As a next step, you’ll look at the classes in the javafx.scene.transform package. These classes define the different transformation types that can be applied to a node. As a base class, the abstract class Transform defines the complete logic and mathematical material that is needed to render a transformation. You’ll also find a set of classes that derive the Transform class in the javafx.scene.transform package. These classes define different transformation types. Figure 4-4 shows an overview of the class hierarchy.
image
image
image
FIGURE 4-4.Class hierarchy of all transformation classes
The Translate, Shear, Rotate, and Scale classes should be used when a simple transformation will be executed. These classes are designed for the special requirements of the different transformation types. If you want to create a general affine transformation, you can use the Affine class. This class defines a general affine transformation, and all the other mentioned transformations are special types of an affine transformation. An affine transformation can be generally described as a transformation between two affine spaces that preserves points, straight lines, and planes. Matrix operations can be performed on instances of the Affine class, allowing this class to provide a better fit for complex transformations.
You can apply a transformation that is defined by the given classes to any node. The following demo application shows the classes you can use:
image
image
image
In the given example, three transforms (rotation, translate, and shearing) will be used to transform a rectangle. The code is a little more complex than the demos shown earlier, so let’s look at it in further depth.
The application consists of two panes. The first pane is a StackPane control that will be used to visualize different transforms. The second pane is an HBox called menu. This pane contains buttons that will handle the action of the application. Whenever one of these buttons is clicked, a list of Transform instances will be executed on a rectangle, which will be shown in the StackPane node. Each button defines a different order of the transforms, and the result of the rectangle will be completely different. This depends on the order in which the transforms are executed on the rectangle. To visualize the steps of transformations, the useTransform(…) method is used. This method adds a rectangle for each transformation step onscreen. Figure 4-5 shows the different results of the transforms. In the screen on the left, the rectangle is rotated, and after the rotation, it is translated. The rotation always has the top-left corner of the rectangle as its center. A rotation of a node affects its complete coordination system. Therefore, the translation in the left screen is executed on a rotated coordination system. In the screen in the middle, the translation is performed before the rotation of the rectangle. In both examples, the same transforms are executed on the rectangle, but the resulting location of the rectangle is different because of the changed order of the transforms. The rightmost screen adds a shearing to the list of transforms.
image
image
image
FIGURE 4-5.Different transformation orders
image
NOTE
In JavaFX, each transformation is represented by a matrix. Each point in the node is multiplied by the matrix to determine its new position. Matrix multiplication is not associative, so changing the order in which the transforms are applied will produce different results, as shown in Figure 4-5.
In most cases, a developer will use the transform methods that the Node class provides directly. It is important to know that the transforms defined by these methods are executed in a given order: translate, scale, and rotate. Additionally, it is important that the pivot of the rotation is not the top-left corner of a node. When using the internal rotation of a node, the pivot point is computed as the center of the node. Figure 4-6 shows an example of a node that is rotated by using the setRotate(45) method. As you can see, the rotation pivot is a different one from in the previous example. All additional transformations that are set on a node by using the transforms property will be applied before the inner transforms of the node are executed. Whenever both types of transformation are mixed, developers have to know this order to avoid any unwanted behavior. It is not necessary to mix the different approaches. As already mentioned, it is a best practice to use the internal methods of a node to transform it.
image
image
image
FIGURE 4-6.A simple rotation
Laying Out Nodes
The earlier examples used panes to lay out groups of nodes in the scene graph. You can also use the transformation of nodes to lay out nodes; however, as a best practice, you should use panes to lay out all the nodes of an application. The transform features are best for defining special behavior, such as that used in animations, for example. Or, an important control can scale or rotate when the mouse button hovers above it to create a better user experience. Still, all these nodes need to be laid out in a pane before a transformation affects them.
In the previous chapters, the Pane class and some concrete implementations such as the StackPane were shown. Figure 4-7 shows a short overview of the class hierarchy. The four pane types (StackPane, BorderPane, AnchorPane, and FlowPane) that are shown in the figure are only examples.
image
image
image
FIGURE 4-7.Class hierarchy of panes
JavaFX provides a large set of default implementations that can be used to lay out an application. While these default implementations meet many needs, developers may require a special layout that can’t be created by using these default implementations. In these situations, you can create a custom pane. The following examples show how to define a custom layout with JavaFX and how the layout algorithms of JavaFX behave internally.
Creating a Custom Pane
To create a pane that lays out its children with a special algorithm, you need a new class that is a subclass of the Pane class. In the following example, a special layout is needed that sorts all child nodes by the preferred width of the node. It should act like an HBox, but all nodes should appear in order of their width instead of the default child order. The following code block shows how a custom pane that implements this feature should look in a first iteration:
image
image
As you can see, there are two kinds of methods that need to be overridden: the layoutChildren(…) method and all the methods that compute the size of the pane. Let’s take a look at all the compute …(…) methods first. Most developers who have worked with UI toolkits know the three different sizes that can be defined for a component: minimum, maximum, and preferred. In JavaFX, all of these types are supported, and the scene graph API offers methods to calculate the width and height of these sizes. A great benefit of these methods is that you can calculate the width depending on the height, and vice versa. I’ll cover this further a bit later, but let’s first consider the simple calculations that don’t depend on each other. All the nodes that are wrapped in the pane should fit in its bounds. To do so, you calculate the preferred width and height of the pane using the methods computePrefHeight(…) and computePrefWidth(…). Because all child nodes should be ordered in a horizontal direction, the preferred height of the pane is equal to the height of the highest child node. This is done in the computePrefHeight(…) method by simply iterating over all the children and finding the maximum height with the help of Math.max(…). The preferred width of the pane is equal to the sum of all the child nodes’ widths. By calculating this, the preferred height of the pane is also calculated. These values are needed to lay out the parent region of the pane once it is added to a scene graph. Each pane in JavaFX should use the calculated sizes (maximum, minimum, and preferred) to calculate its own size. In the given example, you want the pane to always be as big as its children. To achieve this, you can simply override the computeMin …(…) and computeMax…(…) methods by returning the preferred size. Other pane implementations, such as the StackPane, use Double.MAX_VALUE to define the maximum size. Here, the pane can be bigger than its children, for example.
Once all these sizes are calculated, you need the layout of the child nodes. This happens in the layoutChildren() methods that can be overridden by a custom pane. In this example, you create a sorted list of all child nodes and then define the bounds of them. Here, you use the preferred size of a child node and calculate only the X position of the nodes. By doing this, all nodes will appear in a sorted line. To place a node in a pane or region, you can use the layoutInArea(…) method, which has a default implementation to define the bounds of a child. You will take a deeper look at this mechanism later in the chapter. First, you need to know only the first five parameters of this method: the child nodes and its calculated bounds that are defined by its x-position, y-position, width, and height. For the last three parameters, default values are used here.
After you define the SortedPane class, you can simply use it in an application. The following code defines a demo application that uses the SortedPane class. Figure 4-8 shows how the example should look onscreen.
image
image
image
FIGURE 4-8.Visual result of the SortedPane demo
image
image
As you can see in Figure 4-8, all child nodes of the SortedPane appear in a sorted order depending on its visual width. The given example contains a good overview of the basic methods that need to be known to create a custom Pane class, but the layout API of JavaFX offers many more possibilities. In a professional application, the shown mechanisms are not enough to define a pane that fits completely into any JavaFX application. By default, JavaFX provides properties to define the padding and insets of a pane. Additionally, a border can be set to a pane. This border should not hide the underlying child nodes. To strictly follow all the given definitions that are part of the JavaFX layout API, you will need the information in the next section.
Floating-Point Bounds
Developers who have worked with the Swing or AWT toolkit should notice one big difference in JavaFX: All properties that define the bounds of a node are defined as double. The methods of JComponent have their historical background in AWT. At the time of implementation, no one thought about rectangles that were arranged between pixels and were drawn with antialiasing. The JavaFX method provides this functionality, and once transformation comes into play, everyone should understand why this is essential. By using double values, you can define bounds that don’t fit into the pixel-based grid that is forced by a monitor, for example. In JavaFX, the size of a component can be defined by using floating-point bounds, as shown in this illustration.
image
All the rectangles that are shown in the image have a region of 24 pixels when they are rendered onscreen without a scaling transform. The first two rectangles could be defined in Swing too. But the third rectangle is defined with floating-point values. This can be done only in JavaFX.
When a node is rendered, the bound will snap to pixels. This is handled by the snapToPixel property that is defined in the Region class. This Boolean property is set true by default and will adjust the position, spacing, and size of all children of the node to pixel boundaries. Once this property is set to false, a fractional alignment is used, which may lead to “fuzzy-looking” borders.
The Visual Structure of a Region
As stated earlier, each pane and control in JavaFX extend the Region class. This class defines some special properties and behaviors of how it appears onscreen. You need to know these features to lay out custom controls and panes in the right way. These definitions are not reinvented by JavaFX, though. The CSS3 specification of the World Wide Web Consortium (W3C) for backgrounds and borders is used internally. You can find the complete specification at www.w3.org/TR/2012/CR-css3-background-20120724/.
As defined in the specification, a region contains a border area and a padding area for decoration, as shown in Figure 4-9. The insets of a region are defined as a union of these two areas. All child nodes of a region should be laid out inside the content area. The margin that is shown in Figure 4-9 can be used by panes. The HBox, for example, has the static method setMargin(…) to define a margin for a child node inside an HBox instance.
image
image
image
FIGURE 4-9.Areas of a region
All custom panes and controls should use this specification. If you provide custom panes in a third-party library, for example, developers who use the panes normally will expect the described behavior. The SortedPane class that was created earlier doesn’t use the given insets when laying out all child nodes or computing the size of the pane. To fit in the defined specifications, some additions are needed. The following code contains a new version of the SortedPane that internally uses the border and padding insets for calculation:
image
image
image
As mentioned earlier, the insets property of a region is a union of the border area and the padding area. Therefore, this property can be used to calculate the content area of the SortedPane. All child nodes of the pane will now be rendered inside this area. The size of the border and the custom padding of the pane are added to the calculation. In the computePrefHeight(…) and computePrefWidth(…) methods, the insets property is used to calculate the preferred size. The particular values of the property (top/bottom for the height and left/right for the width) are added to the earlier calculated values. As a result, the pane will be as big is its content in union with the border and padding. In the layoutChildren() method, the insets property is used too. The y-position of all components is equal to the height of the insets area, and the x-position of the first child node is equal to the width of the left insets. In JavaFX, different values can be defined for the top, bottom, right, and left of the padding or the border. You can have a border that has a thickness of 3 pixels on the left side and 20 pixels on the right side, for example.
Before you look at the visual result of the SortedPane, you need to implement one additional feature. In the first demo, all child nodes are rendered without any spacing between them. This should be variable. To do so, a spacing property is needed. The property API was mentioned earlier in this book, and some properties have already been used in the examples, but this is the first point where a custom property is added to a class. The following example shows the final version of the SortedPane. Here, a DoubleProperty is added to define the spacing between all child components. The property is defined because it is a best practice in JavaFX. All internal classes of JavaFX define properties, as shown in this example.
image
image
image
image
In addition to the property that is defined, getter and setter methods are defined to access the double value of the property. The spacing value is used in the different methods just as when the insets were added. The following application uses the final version of the SortedPane and adds padding and spacing to the pane. Figure 4-10 shows how the application will look onscreen.
image
image
image
FIGURE 4-10.SortedPane with padding and spacing
image
image
Using Default Values for Size Calculation
In the demo, all methods to compute the minimum, maximum, and preferred sizes are overwritten. However, this doesn’t need to be done in all cases. You can set a default value to all the shown values by using methods such as setPrefHeight(…) or setMinWidth(…). Once a value is set, it will be used instead of calculating a value with the help of the shown methods. In addition, you can use constants to add flexibility: If the preferred width is set by using the setPrefWidth(…) method, it can be simply unset by using the Region.USE_COMPUTED_SIZE constant. After calling setPrefWidth(USE_COMPUTED_SIZE), the layout mechanism will use the computePrefWidth(…) method again. If the minimum or maximum value should be equal to the preferred one, you can use the Region.USE_PREF_SIZE flag. By passing this flag to setMinWidth(…), the layout mechanism will never call computeMinWidth(…). Instead, computePrefWidth(…) will be used. By using these flags, some of the methods of the SortedPane class don’t need to be overwritten. The constructor of the class should simply include the following code snippet:
image
image
Once this is done, you no longer need to overwrite the computeMinWith(…), computeMinHeight(…), computeMaxWidth(…), and computeMaxHeight(…) methods. As a result, the code will be much smaller. This effect can simply be negated by using the Region.USE_COMPUTED_SIZE flag.
Using Properties in Custom Classes
In all custom classes, you should use the JavaFX property API. By doing this, developers get the benefit of all the features, such as binding, that are part of the property API. At first sight, it is a lot of code that needs to be added to a class, but when CSS or other special APIs come into play, properties are needed. Also, in other UI toolkits such as Swing, a lot of code is added if developers want to use values the right way. For example, if property change support is needed in Swing, a PropertyChangeEvent needs to be fired in each setter. This isn’t needed in JavaFX, thanks to the property API, which offers a complete set of default properties.
In the example, the SimpleDoubleProperty class is used. This class is the default implementation for a property that holds a double value. The property instance offers read and write access to the property. In addition to this class, a complete set of default implementations is part of the API, such as SimpleStringProperty. JavaFX contains property classes for all primitive data types. For all value types where no default implementation is defined, the SimpleObjectProperty<T> class can be used. This class uses generics to define the type of its content. If only read access is allowed for a value, developers can use the ReadOnlyProperty classes such as ReadOnlyStringProperty. To add perfect property support to the SortedPane class, some additional changes are needed. Whenever the spacing property is set, the pane will not automatically re-layout its content. To do so, the requestLayout() methods that start a redo need to be called. This could be done by adding a listener to them, as shown in the following code:
image
image
As a first step, this is okay and will work in all cases. But if you want to develop custom panes and controls that follow all the best practices set by the default classes of JavaFX, you need to do this in another way. As you can see in the spacingProperty() method, the instance of the property will be created only when it is needed. As long as only the getSpacing() method is called, for example, the property instance will never be created. This is done to protect resources. A property instance contains some fields that will need memory when a new instance is created. Additionally, a listener is created in the code snippet. This will allocate memory too. This can be avoided by using the invalidated() method of the SimpleDoubleProperty class. This method will be called whenever a new value is set to the property. By overwriting this method as shown in the following code snippet, the requestLayout() method will be called whenever a new spacing value is set. As a result, the property will be created only when it is needed.
image
image
You can test the new behavior and the automatically reformatted layout with the following demo. Here a slider control is added to change the spacing of the SortedPane instance at runtime. Changing the value of the slider will have direct visual feedback because the spacing between the child nodes of the SortedPane will change onscreen.
image
image
Extended Internal Layout Mechanisms
The methods that compute the desired size of a region can handle the width-to-height ratio. Additionally, the layoutInArea(…) method is used internally in the SortedPane example and wasn’t described until now. Since I described the basic mechanisms of layout, I’ll discuss these more specific aspects now. These mechanisms will help to create complex custom layout algorithms in JavaFX.
Handling the Aspect Ratio of a Region
You will sometimes have a control or pane where the width depends on the height of the node, or vice versa. This was a sorely missed feature in the Swing UI toolkit; in Swing, it was difficult to create a dynamic label with line wrap. In a label that supports this behavior, the text will automatically add a page whenever the maximum width of the label is reached. As a result, the height of the label depends on its width. All text components in JavaFX support this behavior because the JavaFX layout can handle the aspect ratio. This can be defined by adding more logic to the methods that will calculate the minimum, maximum, and preferred sizes of a Region instance. To do so, a parameter is defined in the method header. The computePrefWidth(double height) method, for example, adds a height parameter. The method can then be used to calculate a width that depends on the given height. The size of a region can have only one dependency, of course. The height will depend on the width, or the width will depend on the height. Both dependencies can’t logically be handled. To do that, you must define an additional value in the Node class: content bias. This value defines the node’s resizing bias for layout purposes. By default, the getContentBias() method returns null. This means there is no defined ratio; –1 will be used for all parameters of the compute …(…) methods in this case. If the getContentBias() method returns a vertical or horizontal orientation, a ratio is defined, and concrete values are passed to the compute…(…) methods.
Let’s think about a content bias that is defined as a horizontal orientation. In this case, the height of a node depends on its width. When the node needs to have its layout updated, the computePrefWidth(…) method will be called first. Here, –1 is passed to the method, and a default width will be calculated. Based on the maximum, minimum, and preferred widths of the node, the parent region will set a width that will be used. This calculated width will now be used to define the height of the node. To do so, the computePrefHeigth(…) method is called with the calculated width. The implementation of the method can calculate a preferred width that fits the given height. This described behavior is used in the Label control of JavaFX to add page layout, for example. To gain a better understanding of the mechanism, the following example will use aspect ratio.
To show the usage of the content bias value, you will create a custom node. This class extends the Region class and will always have a surface area of a given value. Because the surface area is given, the width of the node depends on its height, or vice versa, depending on its content bias. To show all different variations, the content bias of the node needs to be changeable too. The following class implements the Region class that supports all the necessary features:
image
image
image
image
With contentBias and surfaceArea, two additional properties are added to the class by using the property API. The AreaRegion class overrides the getContentBias() methods and returns the content bias that is defined by the included property. All compute…(…) methods that are needed to calculate the size of the node are overridden. In this method, the aspect ratio is defined. This can be easily done by adding an if-else statement to the code. When the internal content bias is set to Orientation.VERTICAL, all methods that compute a width will be called with a given height. Otherwise, the height is –1, and a default value can be used. Whenever the content bias or the surface area is set, the parent region of the instance needs to be laid out. To do this, the requestLayout() method is called in the invalidated() method of the properties.
The following application uses the AreaRegion class and adds some controls to change its behavior:
image
image
CSS is used in this example to define attributes that specify the way the AreaRegion is displayed. The CSS string defines a black border and a blue background for the node. This is done here only to render the AreaRegion onscreen in a special color. CSS will be explained later in the book.
When the application is started, the content bias of the AreaRegion can be changed by two buttons. Additionally, the surface area can be set by a slider. Here, the application takes advantage of the property API’s benefits by simply binding the surface area to the value of the slider. Because this is a dynamic example, no screenshot is shown here. When running the demo and changing some of its input values, you can see how the different content bias will affect the layout of the AreaRegion inside the StackPane. By simply changing the size of the application window, the bounds of the AreaRegion will change by always fitting the given surface area.
Additional Layout Mechanisms
All the mechanisms that decide which compute method will be used and how the content bias will be interpreted are hidden by the layoutInArea(…) method. By using the method inside the layoutChildren() method, developers can simply define an area in which a defined child component should be laid out. Internally, the method will set the bounds of the child node by calling Node.resize(double width, double height) and Node.relocate(double x, double y). In most cases, a developer should use either the layoutInArea(…) or positionInArea(…) method of the Region class. The difference between these two methods is simple: positionInArea(…) never changes the size of a child node; it only sets the location. In most cases, layoutInArea(…) will be used. To prevent the resizing of a node, the Node.isResizable() method can be overwritten. As a result, a node can control this behavior by itself. A button returns true here because the size of a button can change. A rectangle returns false here. If a rectangle is defined with a given size, such as is done with new Rectangle(40,40), it should not change its size.
The javafx.geometry Package
In older Java-based UI toolkits, geometrical definitions such as alignments or orientations were defined by static constants in the code. Often, integer values were used. All these toolkits were created before enumerations were added to the Java language. In JavaFX, all these definitions are defined by enums in the javafx.geometry package. Some of them, such as the Orientation enum, have already been used in the demo applications in this chapter. Table 4-2 describes the enums that are part of this package.
image
TABLE 4-2.Geometrical Enums
In addition to the enums, this package defines some helper classes such as Dimension2D or Point3D. Whenever it makes sense, these classes should be used in code.
Working with Constraints
When creating a more complex custom Pane class, as shown in the previous example, constraints can be useful. Constraints describe the layout properties of a child node inside a specific pane in order to define a more specialized layout of the node’s children. More complex panes such as the GridPane make extensive use of this feature. A simpler example is the margin inside an HBox. To apply a custom margin on a child node of an HBox, you can call the static method setMargin(Node child, Insets value) and define the child node and its special margin. An instance of HBox will use this margin whenever the child node is laid out. Custom constraints can be easily added to a pane by using the two static methods: Pane.setConstraint(Node node, Object key, Object value) and Pane.getConstraint(Node node, Object key).
Combining Transforms and Layout
This chapter has shown two different JavaFX APIs to render nodes in the defined bounds on the screen. Usually, the complete layout of nodes inside the scene graph is done by using the Region and Pane classes. Additionally, transforms can be applied to a node. Furthermore, you can mix these two APIs without any problems. The following example shows how a child of the SortedPane can be transformed:
image
image
In this application, a translation and rotation transform is defined for one of the buttons that is wrapped in the SortedPane. Figure 4-11 shows how the application will be rendered onscreen. As you can see, the Hello World button is transformed after the layout of the SortedPane is done. Additionally, another feature of the scene graph is shown here: A child node can be rendered outside the bounds of its parent. This isn’t possible in older UI toolkits such as Swing where each container has a defined canvas and can’t render any content outside this canvas. Because a container renders all its child components in Swing, any content that is not inside the area of the canvas will be cut off on the screen because it is never rendered on it.
image
image
image
FIGURE 4-11.Mixing layout and transformations
Accessing the Bounds of a Node
The bounds of a node are normally set by its parent. You can easily access these bounds by using the getBoundsInLocal() method. Once a transform is applied on the node, the bounds onscreen will change, but sometimes you want to access the bounds of a transformed node, so you can use the getBoundsInParent() method. Figure 4-12 shows an example of a rectangle that is transformed and the different results of the two methods.
image
image
image
FIGURE 4-12.Difference between getBoundsInParent() and getBoundsInLocal()
Summary
Earlier chapters described the basic mechanism of node hierarchies in the scene graph, as well as how simple applications can be created by using Scene Builder and default Pane classes. This chapter went one step further and showed how you can create custom transformations and layout. Most of the time, developers don’t need to create custom panes to lay out controls onscreen, but it is important to know at least the basic mechanism of layout and how padding and the border area can affect the bounds of nodes. Additionally, transformations can be helpful when you need a special user experience or better feedback. Combined with animations, transformations will help you create awesome applications with modern interactions. In addition, the chapter described the internal mechanism of layout in JavaFX that you will need whenever you create a custom pane or control. When custom controls are discussed later in this book, all the content of this chapter will be important to understand.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.79.33