Chapter 4
IN THIS CHAPTER
Using five popular layout pane classes: HBox, VBox, FlowPane, BorderPane, and GridPane
Adjusting the size of layout panes and the nodes they contain
Fiddling with various options for spacing out the nodes in a layout pane
Controlling the layout of components in a scene is often one of the most difficult aspects of working with JavaFX. In fact, at times it can be downright exasperating. Often the components almost seem to have minds of their own. They get stubborn and refuse to budge. They line up on top of one another when you want them to be side by side. You make a slight change to a label or text field, and the whole scene seems to rearrange itself. At times, you want to put your fist through the monitor.
The problem isn’t with the components; it’s with the layout panes, which determine where each component appears in its frame or panel. Layout panes are special classes whose sole purpose in life is to control the arrangement of the nodes that appear in a scene. JavaFX provides several distinct types of layout panes; each type uses a different approach to controlling the arrangement of nodes. The trick to successfully lay out a scene is to use the layout panes in the correct combination to achieve the arrangement you want.
Understanding layout panes is the key to creating JavaFX frames that are attractive and usable.
JavaFX provides many different layout panes for you to work with. I explain the following five in this chapter:
HBox
: This layout pane arranges nodes horizontally, one next to the other. You use it to create controls arranged neatly in rows.VBox
: This layout pane arranges nodes vertically, one above the other. You use it to create controls arranged neatly in columns.FlowPane
: This layout pane arranges nodes next to each other until it runs out of room; then, it wraps to continue layout nodes. You can configure a FlowPane
to arrange nodes horizontally in rows or vertically in columns.Border
: This layout pane divides the pane into five regions: Top, Left, Center, Right, and Bottom. When you add a node, you can specify which region you want to place the node in.GridPane
: This layout pane divides the pane into a grid, affording you complete control of the arrangement of elements in rows and columns.To give you a general idea of the results that can be achieved with the first four layout types, Figure 4-1 shows four sample windows that each use one of the layout panes. You’ll see a detailed example of how the GridPane
layout works later in this chapter.
FIGURE 4-1: Four commonly used types of layout panes.
The basic process of working with layout panes is simple. Here is the general procedure for creating a layout node:
Create the controls or other nodes you want to add to the pane.
For example, if the layout pane will contain two buttons, you should create the two buttons using code similar to this:
Button btnOK = new Button();
btnOK.setText("OK");
btnOK.setOnAction( e -> btnOK_Click() );
Button btnCancel = new Button();
btnCancel.setText("Cancel");
btnCancel.setOnAction( e -> btnCancel_Click() );
Create a layout pane by calling its constructor.
For example:
HBox pane = new HBox();
Fine-tune the optional settings used by the layout pane.
Each type of layout pane has a unique assortment of optional parameters that govern the details of how nodes are laid out within the pane. For example, the HBox
pane lets you set the number of pixels that will be used to separate each node in the pane. You can set this value as follows:
HBox.setSpacing(10);
Add each of the nodes that will appear in the layout pane.
Each type of layout pane provides a method for adding nodes to the pane. For the HBox
pane, you must first call the getChildren
method to get a list of all the nodes that have been added to the pane. Then, you call the addAll
method to add one or more nodes to the pane. For example:
pane.getChildren().addAll(btnOK, btnCancel);
Create the scene, specifying the layout pane as the scene’s root node.
For example:
Scene scene = new Scene(pane, 300, 400);
In this example, pane
is added as the root node for the scene.
You can combine several layout panes to create layouts that are more complicated than a single layout pane can provide. For example, suppose you want to create a layout that has a horizontal row of buttons at the bottom and a vertical column of buttons at the right. To do that, you could create an HBox
for the buttons at the bottom and a VBox
for the buttons at the right. Then, you could create a BorderPane
and add the HBox
to the bottom region and the VBox
to the right region.
Combinations like this are possible because all the layout panes inherit the base class javafx.scene.layout.Pane
, which in turn inherits the class javafx.scene.node
. In other words, all panes are also nodes. Each node that you add to a layout pane can be another layout pane. You can nest layout panes within layout panes as deeply as you need to achieve the exact layout you need for your application.
The HBox
class provides one of the simplest of all JavaFX’s layout managers: It arranges one or more nodes into a horizontal row. Table 4-1 presents the most commonly used constructors and methods of the HBox
class.
TABLE 4-1 HBox Constructors and Methods
Constructor |
Description |
|
Creates an empty |
|
Creates an empty |
|
Creates an |
|
Creates an |
Method |
Description |
|
Returns the collection of all child nodes that have been added to the |
|
Sets the alignment for child nodes within the See Table 4-5 for an explanation of the |
|
Sets the growth behavior of the given child node. See Table 4-3 for an explanation of the |
|
Sets the margins for a given child node. See Table 4-2 for the constructors of the For more information, see the section “Adding Space with Margins” later in this chapter. |
|
Sets the padding around the inside edges of the See Table 4-2 for the constructors of the For more information, see the section “Spacing Things Out” later in this chapter. |
|
Sets the spacing between nodes displayed within the For more information, see the section “Spacing Things Out” later in this chapter. |
The HBox
class is defined in the javafx.scene.layout
package, so you should include the following import
statement in any program that uses an HBox
:
import javafx.scene.layout.*;
The easiest way to create an HBox
is to first create the nodes that you want to place in the HBox
and then call the HBox
constructor and pass the nodes as arguments. For example:
Button btn1 = new Button("Button One");
Button btn2 = new Button("Button Two");
Button btn3 = new Button("Button Three");
HBox hbox = new HBox(btn1, btn2, btn3);
If you prefer to create the HBox control in an initially empty state and later add the controls, you can do so like this:
HBox hbox = new HBox();
Hbox.getChildren().addAll(btn1, btn2, btn3);
Here, the getChildren
method is called, which returns a collection of all the children added to the HBox
pane. This collection is defined by the class ObservableList
, which includes a method named addAll
that you can use to add one or more nodes to the list.
By default, child nodes in a layout pane are arranged immediately next to one another, with no empty space in between. If you want to provide space between the nodes in the pane, you can do so in four ways:
In this section, I show you how to add spacing and padding to a pane. Then, the next three sections show you how to use the other two techniques.
Note that although I illustrate the techniques in these sections using the HBox
layout pane, the techniques apply to other types of panes as well.
To set the spacing for an HBox
pane, you can use the spacing parameter on the HBox
constructor or by calling the setSpacing
method. For example, this statement creates an HBox
pane with a default spacing of 10 pixels:
HBox hbox = new HBox(10);
This example creates an HBox
pane with 10-pixel spacing and adds three buttons:
HBox hbox = new HBox(10, btn1, btn2, btn3);
And this example creates an HBox
pane using the default constructor, and then calls the setSpacing
method to set the spacing to 10 pixels:
HBox hbox = new HBox();
Hbox.setSpacing(10);
Although spacing adds space between nodes in an HBox
pane, it doesn’t provide any space between the nodes and the edges of the pane itself. For example, if you set the spacing to 10 pixels and add three buttons to the pane, the three buttons will be separated from one another by a gap of 10 pixels. However, there won’t be any space at all between the left edge of the first button and the left edge of the pane itself. Nor will there be any space between the top of the buttons and the top of the pane. In other words, the three buttons will be crowded tightly into the pane.
To add space around the inside perimeter of the layout pane, use the setPadding
method. This method takes as a parameter an object of type Insets
, which represents the size of the padding (in pixels) for the top, right, bottom, and left edge of an object. You can create an Insets
object using either of the two constructors listed in Table 4-2. The first provides an even padding for all four edges of an object; the second lets you set a different padding value for each edge.
TABLE 4-2 Insets Constructors
Constructor |
Description |
|
Creates an |
|
Creates an |
To set the padding to a uniform 10 pixels, call the setPadding
method like this:
hbox.setPadding(new Insets(10));
To set a different padding value for each edge, call it like this:
hbox.setPadding(new Insets(20, 10, 20, 10));
In this example, the top and bottom padding is set to 20 and the right and left padding is set to 10.
The Insets
class is defined in the javafx.geometry
package, so you should include the following import
statement in any program that uses Insets
:
import javafx.geometry.*;
Another way to add space around the nodes in a layout pane is to create margins around the individual nodes. This technique allows you to set a different margin size for each node in the layout pane, giving you complete control over the spacing of each node.
To create a margin, call the setMargin
method for each node you want to add a margin to. You might think that because each node can have its own margin, the setMargin
method would belong to the Node
class. Instead, the setMargin
method is defined by the HBox
class. The setMargin
method accepts two parameters:
Insets
object that defines the margins you want to addHere’s an example that sets a margin of 10 pixels for all sides of a button named btn1
:
Hbox.setMargin(btn1, new Insets(10));
Here’s an example that sets a different margin for each side of the pane:
Hbox.setMargin(btn1, new Insets(10, 15, 20, 10));
In this example, the top margin is 10 pixels, the right margin is 15 pixels, the bottom margin is 20 pixels, and the left margin is 10 pixels.
Note that margins, spacing, and padding can work together. Thus, if you create a 5-pixel margin on all sides of two buttons, add those two buttons to a pane whose spacing is set to 10 pixels and whose padding is set to 10 pixels, the buttons will be separated from one another by a space of 20 pixels and from the inside edges of the pane by 15 pixels.
A third way to add space between nodes in an HBox
is to create a node whose sole purpose is to add space between two HBox
nodes. Then, you can configure the spacer node that will automatically grow to fill any extra space within the pane. By configuring only the spacer node and no other nodes in this way, only the spacer node will grow. This has the effect of pushing the nodes on either side of the spacer node apart from one another.
For example, suppose you want to create an HBox
layout pane that contains three buttons. Instead of spacing all three buttons evenly within the pane, you want the first two buttons to appear on the left side of the pane and the third button to appear on the right side of the pane. The amount of space between the second and third buttons will depend entirely on the size of the pane. Thus, if the user drags the window to expand the stage, the amount of space between the second and third buttons should increase accordingly.
The easiest way to create a spacer node is by using the Region
class. The Region
class is the base class for both the Control
class, from which controls such as Button
and Label
derive. It is also the base class for the Pane
class, from which all the layout panes described in this chapter derive.
For my purposes here, I just use the simple default constructor of the Region
class to create a node that serves as a simple spacer in a layout pane. I don’t provide a specific size for the region. Instead, I configure it so that it will grow horizontally to fill any unused space within its container.
To do that, you use the static setHgrow
method of the HBox
class, specifying one of the three constant values defined by an enumeration named Priority
enumeration. Table 4-3 lists these constants and explains what each one does.
The Priority
enumeration is defined in the javafx.scene.layout
package; the same package that defines the layout managers that require it.
TABLE 4-3 The Priority Enumeration
Constant |
Description |
|
Indicates that the width of the node should never be adjusted to fill the available space in the pane. This is the default setting. Thus, by default, nodes are not resized based on the size of the layout pane that contains them. |
|
Indicates that the width of the node should always be adjusted if necessary to fill available space in the pane. If you set two or more nodes to |
|
Indicates that the node’s width may be adjusted if necessary to fill out the pane. However, the adjustment will be made only if there are no other nodes that specify |
The following example creates three buttons and a spacer, set the margins for all three buttons to 10 pixels, and then add the three buttons and the spacer to an HBox
such that the first two buttons appear on the left of the HBox
and the third button appears on the right:
// Create the buttons
Button btn1 = new Button("One");
Button btn2 = new Button("Two");
Button btn3 = new Button("Three");
// Create the spacer
Region spacer = new Region();
// Set the margins
HBox.setMargin(btn1, new Insets(10));
HBox.setMargin(btn2, new Insets(10));
HBox.setMargin(btn3, new Insets(10));
// Set the Hgrow for the spacer
HBox.setHgrow(spacer, Priority.ALWAYS);
// Create the HBox layout pane
HBox hbox = new HBox(10, btn1, btn2, spacer, btn3);
Figure 4-2 shows how this pane appears when added to a stage. So that you can see how the spacer works, the first shows three incarnations of the pane, each with the window dragged to a different size. Notice how the spacing between the second and third buttons is adjusted automatically so that the first two buttons are on the left side of the pane and the third button is on the right.
Like the setMargin
method, setHgrow
is a static method, so it should be called from the class, not an instance of the class.
FIGURE 4-2: Using a spacer node to space out buttons in an HBox pane.
The VBox
class is similar to the HBox
class, but instead of arranging nodes horizontally in a row, it arranges them vertically in a column. Table 4-4 shows the most commonly used constructors and methods of the VBox
class.
TABLE 4-4 VBox Constructors and Methods
Constructor |
Description |
|
Creates an empty |
|
Creates an empty |
|
Creates an |
|
Creates a |
Method |
Description |
|
Returns the collection of all child nodes that have been added to the |
|
Sets the alignment for child nodes within the See Table 4-5 for an explanation of the |
|
Sets the margins for a given child node. See Table 4-2 for the constructors of the |
|
Sets the padding around the inside edges of the See Table 4-2 for the constructors of the |
|
Sets the growth behavior of the given child node. See Table 4-3 for an explanation of the |
The VBox
class is defined in the javafx.scene.layout
package, so you should include the following import
statement in any program that uses a VBox
:
import javafx.scene.layout.*;
Here’s an example that creates three buttons and uses a VBox
to arrange them into a column:
Button btn1 = new Button("Button One");
Button btn2 = new Button("Button Two");
Button btn3 = new Button("Button Three");
VBox vbox = new VBox(btn1, btn2, btn3);
You can accomplish the same thing by using the default constructor and calling the getChildren
method, as in this example:
VBox vbox = new VBox();
vbox.getChildren().addAll(btn1, btn2, btn3);
As with the HBox
class, you can use spacing, padding, margins, and spacer nodes to control the spacing of nodes within a VBox
. Here’s an example that sets 10 pixels of vertical space between nodes and 10 pixels of padding on each edge of the pane:
Button btn1 = new Button("One");
Button btn2 = new Button("Two");
Button btn3 = new Button("Three");
VBox vbox = new VBox(10, btn1, btn2, btn3);
vbox.setPadding(new Insets(10));
Here’s an example that creates a column of three buttons, with one button at the top of the column and two at the bottom, with 10 pixels of spacing and padding:
// Create the buttons
Button btn1 = new Button("One");
Button btn2 = new Button("Two");
Button btn3 = new Button("Three");
// Create the spacer
Region spacer = new Region();
// Set the Vgrow for the spacer
VBox.setVgrow(spacer, Priority.ALWAYS);
// Create the VBox layout pane
VBox vbox = new VBox(10, btn1, spacer, btn2, btn3);
vbox.setPadding(new Insets(10));
Both the HBox
and the VBox
layout panes have a setAlignment
method that lets you control how the nodes that are contained within the pane are aligned with one another. The setAlignment
method accepts a single argument, which is one of the constants defined by the Pos
enumeration, described in Table 4-5.
The Pos
enumeration is defined in the javafx.geometry
package, so you should include the following import
statement in any program that uses Pos
:
import javafx.geometry.*;
The following example shows how you might create a vertical column of three buttons, centered within the pane:
Button btn1 = new Button("Number One");
Button btn2 = new Button("Two");
Button btn3 = new Button("The Third Button");
VBox vbox = new VBox(10, btn1, btn2, btn3);
vbox.setPadding(new Insets(10));
vbox.setAlignment(Pos.CENTER);
TABLE 4-5 The Pos Enumeration
Constant |
Vertical Alignment |
Horizontal Alignment |
|
Top |
Left |
|
Top |
Center |
|
Top |
Right |
|
Center |
Left |
|
Center |
Center |
|
Center |
Right |
|
Bottom |
Left |
|
Bottom |
Center |
|
Bottom |
Right |
|
Baseline |
Left |
|
Baseline |
Center |
|
Baseline |
Right |
When this pane is added to a scene and then shown in a stage, the results resemble the window shown in Figure 4-3.
FIGURE 4-3: Three buttons centered in a VBox layout pane.
The flow layout comes in two flavors: horizontal and vertical. A horizontal flow layout arranges its child nodes in a row until the width of the pane reaches a certain size that you can specify. When that size is reached, the layout begins a new row of child nodes beneath the first row. This flow continues, starting a new row each time the size limit is reached, until all the child nodes have been placed.
A vertical flow layout works the same way except that child nodes are laid out in columns until the size limit is reached. When the size limit is reached, a new column immediately to the right of the first column is started.
You use the FlowPane
class to create a flow layout. Table 4-6 shows the constructors and most commonly used methods for the FlowPane
class.
TABLE 4-6 FlowPane Constructors and Methods
Constructor |
Description |
|
Creates an empty horizontal flow layout with both the horizontal and vertical gaps set to zero. |
|
Creates an empty horizontal |
|
Creates a horizontal flow layout with the specified horizontal and vertical gaps and populated with the specified child nodes. |
|
Creates a horizontal flow layout with both the horizontal and vertical gaps set to zero and populated with the specified child nodes. |
Note: In each of the following constructors, |
|
|
Creates an empty flow layout with the specified orientation and both the horizontal and vertical gaps set to zero. |
|
Creates an empty flow layout with the specified orientation and the specified horizontal and vertical gaps. |
|
Creates a flow layout with the specified orientation and horizontal and vertical gaps, populated with the specified children. |
|
Creates a flow layout with the specified orientation and both the horizontal and vertical gaps set to zero, populated with the specified children. |
Method |
Description |
|
Returns the collection of all child nodes. The collection is returned as an |
|
Sets the alignment for nodes within the rows and columns. See Table 4-5 for an explanation of the |
|
Sets the alignment for nodes within the columns. See Table 4-5 for an explanation of the |
|
Sets the horizontal gap. For a horizontal flow layout, this is the amount of space between nodes. For a vertical flow layout, this is the amount of space between columns. |
|
Sets the margins for a given child node. See Table 4-2 for the constructors of the |
|
Sets the orientation of the flow layout, which can be |
|
Sets the padding around the inside edges of the See Table 4-2 for the constructors of the |
|
Sets the preferred wrap length for the pane. For a horizontal flow layout, this represents the preferred width of the pane; for a vertical flow layout, it represents the preferred height. |
|
Sets the alignment for nodes within the rows. See Table 4-5 for an explanation of the |
|
Sets the vertical gap. For a vertical flow layout, this is the amount of space between nodes. For a horizontal flow layout, this is the amount of space between rows. |
The FlowPane
class is defined in the javafx.scene.layout
package, so you should include the following import
statement in any program that uses a flow layout:
import javafx.scene.layout.*;
The constructors for this class let you specify the horizontal and vertical gaps, which provide the spacing between the horizontal and vertical elements of the layout, the orientation (horizontal or vertical), and the child nodes with which to populate the layout.
To set the limit at which the flow layout wraps, you use the setPrefWrapLength
method. The wrap length is applied to the dimension in which the pane flows its contents. Thus, for a horizontal flow layout, the wrap length specifies the preferred width of the pane; for a vertical flow layout, the wrap length specifies the pane’s preferred height.
Note that regardless of the preferred wrap length, if you don’t call this method, the wrap length defaults to 400 pixels.
The following example creates a horizontal layout with 10 pixels of horizontal and vertical gaps, populated by five buttons, and a preferred wrap length of 300 pixels:
Button btn1 = new Button("Button One");
Button btn2 = new Button("Button Two");
Button btn3 = new Button("Button Three");
Button btn4 = new Button("Button Four");
Button btn5 = new Button("Button Five");
FlowPane pane = new FlowPane(Orientation.HORIZONTAL,
10, 10, btn1, btn2, btn3, btn4, btn5);
pane.setPrefWrapLength(300);
Figure 4-4 shows how these buttons appear when the layout is added to a scene and the scene displayed in a stage. This figure also shows how the buttons in the flow layout are rearranged when the user resizes the window. Notice that initially, the first three buttons appear on the first row and the next two appear on the second row. When the window is dragged a bit wider, the buttons reflow so that four fit on the first row and just one spills to the second row. Then, when the window is dragged smaller, just two buttons appear on the first two rows and a third row is created for the fifth button.
FIGURE 4-4: A flow layout pane with five buttons.
The border layout is a pane that is carved into five regions: Top, Left, Center, Right, and Bottom, as shown in Figure 4-5. When you add a component to the layout, you can specify which of these regions the component goes in.
FIGURE 4-5: How the border layout carves things up.
You use the BorderPane
class to create a border layout. Table 4-7 lists the constructors and the most commonly used methods for the BorderPane
class.
TABLE 4-7 BorderPane Constructors and Methods
Constructor |
Description |
BorderPane () |
Creates an empty border layout. |
BorderPane (Node center) |
Creates a border layout with the specified center node. |
BorderPane (Node center, Node top, Node right, Node bottom, Node left) |
Creates a border layout with the specified center, top, right, bottom, and left nodes. |
Method |
Description |
void setCenter(Node node) |
Sets the center node. |
void setTop(Node node) |
Sets the top node. |
void setRight(Node node) |
Sets the right node. |
void setBottom(Node node) |
Sets the bottom node. |
void setLeft(Node node) |
Sets the left node. |
void setAlignment(Pos alignment) |
Sets the alignment for nodes within border pane. See Table 4-5 for an explanation of the |
static void setMargin(Node child, Insets value) |
Sets the margins for a given child node. See Table 4-2 for the constructors of the |
The BorderPane
class is defined in the javafx.scene.layout
package, so you should include the following import
statement in any program that uses a border layout:
import javafx.scene.layout.*;
The default constructor for this class creates an empty border layout, to which you can add nodes later, as in this example:
Button btn1 = new Button("Button One");
Button btn2 = new Button("Button Two");
Button btn3 = new Button("Button Three");
VBox vbox = new VBox(btn1, btn2, btn3);
BorderPane pane = new BorderPane();
pane.setCenter(vbox);
Here, three buttons are created and added to a VBox
. Then, a border layout is created, and the VBox
is added to its center region.
Alternatively, you can add a node to the center region via the BorderPane
constructor, like this:
BorderPane pane = new BorderPane(vbox);
The third constructor listed in Table 4-7 lets you add nodes to all five regions at once. The following example assumes that you have already created five panes, named centerPane
, topPane
, rightPane
, bottomPane
, and leftPane
:
BorderPane pane = new BorderPane(centerPane,
topPane, rightPane, bottomPane, leftPane);
The border layout regions are sized according to their contents.
Thus, if you add a VBox
pane to the right region, the width of the VBox
pane will determine the width of the right region.
If the user resizes the window to make it wider, the top, center, and bottom regions will expand in width — the width of the left and right regions remains unchanged.
Similarly, if the user drags the window to make it taller, the left, center, and right regions expand in height; the height of the top and bottom regions remains the same.
The grid pane layout manager lets you arrange GUI elements in a grid of rows and columns. Unlike a tile pane, the rows and columns of a grid pane do not have to be the same size. Instead, the grid pane layout automatically adjusts the width of each column and the height of each row based on the components you add to the panel.
Here are some important features of the grid pane layout manager:
GridPane
to stretch a component to fill the entire space allotted to it if the component isn’t already big enough to fill the entire area. You can specify that this stretching be done horizontally, vertically, or both.The following sections describe the ins and outs of working with grid pane layouts.
Before you create a grid pane layout, draw a sketch showing how you want the components to appear in the panel. Then slice the panel into rows and columns, and number the rows and columns starting with zero in the top-left corner. Figure 4-6 shows such a sketch for an application that lets a user order a pizza.
FIGURE 4-6: Sketching out a panel.
After you have the panel sketched out, list the components, their x and y coordinates on the grid, their alignment, and whether each component spans more than one row or column. Here’s an example:
Component |
x |
y |
Alignment |
Spans |
Label |
0 |
0 |
Right |
|
Label |
0 |
1 |
Right |
|
Label |
0 |
2 |
Right |
|
Name text field |
1 |
0 |
Left |
2 |
Phone text field |
1 |
1 |
Left |
2 |
Address text field |
1 |
2 |
Left |
2 |
Size radio buttons |
0 |
3 |
Left |
|
Style radio buttons |
1 |
3 |
Left |
|
Toppings check boxes |
2 |
3 |
Left |
|
OK and Close buttons |
2 |
4 |
Right |
After you lay out the grid, you can write the code to put each component in its proper place.
Table 4-8 shows the most frequently used constructors and methods of the GridPane
class, which you use to create a grid pane.
To create a basic grid pane, you first call the GridPane
constructor. Then, you use the add
method to add nodes to the grid pane’s cells. The parameters of the add
method specify the node to be added, the node’s column index, and the node’s row index. For example, the following code snippet creates a label, and then creates a grid pane and adds the label to the cell at column 0, row 0:
Label lblName = new Label("Name");
GridPane grid = new GridPane();
grid.add(lblName, 0, 0);
TABLE 4-8 GridPane Constructors and Methods
Constructor |
Description |
|
Creates an empty grid pane. |
Method |
Description |
|
Adds a node at the specified column and row index. |
|
Adds a node at the specified column and row index with the specified column and row spans. |
|
Adds an entire column of nodes. |
|
Adds an entire row of nodes. |
|
Returns the column constraints. For more information, see to Table 4-6. |
|
Returns the row constraints. For more information, see Table 4-7. |
|
Sets the column span for the specified node. |
|
Sets the row span for the specified node. |
|
Sets the horizontal alignment for the node. Allowable values are |
|
Sets the vertical alignment for the node. Allowable values are |
|
Sets the size of the gap that appears between columns. |
|
Sets the size of the gap that appears between rows. |
|
Sets the margin for a particular node. See Table 4-2 earlier in this chapter for an explanation of the |
|
Sets the padding around the inside edges of the grid pane. See Table 4-2 earlier in this chapter for an explanation of the |
|
Sets the minimum height of the grid pane. |
|
Sets the maximum height of the grid pane. |
|
Sets the preferred height of the grid pane. |
|
Sets the minimum width of the grid pane. |
|
Sets the maximum width of the grid pane. |
|
Sets the preferred width of the grid pane. |
The typical way to fill a grid pane with nodes is to call the add
method for each node. However, if you prefer, you can add an entire column or row of nodes with a single call to either addColumn
or addRow
. For example, this example creates a label and a text field, and then creates a grid pane and adds the label and the text field to the first row:
Label lblName = new Label("Name");
TextField txtName = new TextField();
GridPane grid = new GridPane();
grid.addRow(0, lblName, txtName);
If a node should span more than one column, you can call the setColumnSpan
method to specify the number of columns the node should span. For example:
GridPane.setColumnSpan(txtName, 2);
Here, the txtName
node will span two columns. You use the setRowSpan
in a similar way if you need to configure a node to span multiple rows.
To control the horizontal alignment of a node, use the setHalignment
method as in this example:
GridPaNE.setHalignment(lblName, HPos.RIGHT);
Here, the lblName
node is right-aligned within its column. The setValignment
method works in a similar way.
Like other layout panes, the GridPane
class has a host of methods for setting spacing and alignment details. You can use the setHgap
and setVgap
methods to set the spacing between rows and columns so that your layouts won’t look so cluttered. You can use the setPadding
and setMargins
methods to set padding and margins, which work just as they do with other layout panes. And you can set the minimum, maximum, and preferred width and height for the grid pane.
You can control most aspects of a grid pane’s layouts using methods of the GridPane
class, but unfortunately, you can’t control the size of individual columns or rows. To do that, you must use the ColumnConstraints
or RowConstraints
class, as described in Tables 4-9 and 4-10.
TABLE 4-9 The ColumnConstraints Class
Constructor |
Description |
|
Creates an empty column constraints object. |
|
Creates a column constraint with a fixed width. |
|
Creates a column constraint with the specified minimum, preferred, and maximum widths. |
Method |
Description |
|
Sets the minimum width of the column. |
|
Sets the maximum width of the column. |
|
Sets the preferred width of the column. |
|
Sets the width as a percentage of the total width of the grid pane. |
|
Determines whether the width of the column should grow if the grid pane’s overall width increases. Allowable values are |
|
If true, the grid pane will expand the nodes within this column to fill empty space. |
|
Sets the horizontal alignment for the entire column. Allowable values are |
To use column constraints to set a fixed width for each column in a grid pane, first create a constraint for each column. Then, add the constraints to the grid pane’s constraints collection. Here’s an example:
ColumnConstraints col1 = new ColumnConstraints(200);
ColumnConstraints col2 = new ColumnConstraints(200);
ColumnConstraints col3 = new ColumnConstraints(200);
GridPane grid = new GridPane();
grid.getColumnConstraints().addAll(col1, col2, col3);
TABLE 4-10 The RowConstraints Class
Constructor |
Description |
|
Creates an empty row constraints object. |
|
Creates a column constraint with a fixed height. |
|
Creates a column constraint with the specified minimum, preferred, and maximum heights. |
Method |
Description |
|
Sets the minimum height of the row. |
|
Sets the maximum height of the row. |
|
Sets the preferred height of the row. |
|
Sets the height as a percentage of the total height of the grid pane. |
|
Determines whether the height of the row should grow if the grid pane’s overall height increases. Allowable values are |
|
If |
|
Sets the horizontal alignment for the entire row. Allowable values are |
Column constraints are matched to their corresponding columns based on the collection of constraints added to the GridPane
. Thus, in the preceding example, the col1
constraint will be applied to the first column, col2
will be applied to the second column, and col3
will be applied to the third column.
One of the most useful features of column constraints is their ability to distribute the width of a grid pane's columns as a percentage of the overall width of the grid pane. For example, suppose the grid pane will consist of three columns and you want them to all be of the same width regardless of the width of the grid pane. The following code accomplishes this:
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(33);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(33);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(33);
GridPane grid = new GridPane();
grid.getColumnConstraints().addAll(col1, col2, col3);
In this example, each column will fill 33 percent of the grid.
Listing 4-1 shows the code for a program that displays the scene I drew for Figure 4-7, and Figure 4-7 shows how this scene appears when the program is run. Figure 4-7 shows that the final appearance of this scene is pretty close to the way I sketched it.
FIGURE 4-7: The Pizza Order application in action.
LISTING 4-1 The Pizza Order Application
package com.lowewriter.PizzaOrder;
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;
public class PizzaOrder extends Application
{
public static void main(String[] args)
{
launch(args);
}
Stage stage;
TextField txtName;
TextField txtPhone;
TextField txtAddress;
RadioButton rdoSmall;
RadioButton rdoMedium;
RadioButton rdoLarge;
RadioButton rdoThin;
RadioButton rdoThick;
CheckBox chkPepperoni;
CheckBox chkMushrooms;
CheckBox chkAnchovies;
@Override public void start(Stage primaryStage)
{
stage = primaryStage;
// Create the name label and text field→34
Label lblName = new Label("Name:");
txtName = new TextField();
txtName.setMinWidth(100);
txtName.setPrefWidth(200);
txtName.setMaxWidth(300);
txtName.setPromptText("Enter the name here");
// Create the phone number label and text field→42
Label lblPhone = new Label("Phone Number:");
txtPhone = new TextField();
txtPhone.setMinWidth(60);
txtPhone.setPrefWidth(120);
txtPhone.setMaxWidth(180);
txtPhone.setPromptText("Enter the phone number here");
// Create the address label and text field→50
Label lblAddress = new Label("Address:");
txtAddress = new TextField();
txtAddress.setMinWidth(100);
txtAddress.setPrefWidth(200);
txtAddress.setMaxWidth(300);
txtAddress.setPromptText("Enter the address here");
// Create the size pane→58
Label lblSize = new Label("Size");
rdoSmall = new RadioButton("Small");
rdoMedium = new RadioButton("Medium");
rdoLarge = new RadioButton("Large");
rdoMedium.setSelected(true);
ToggleGroup groupSize = new ToggleGroup();
rdoSmall.setToggleGroup(groupSize);
rdoMedium.setToggleGroup(groupSize);
rdoLarge.setToggleGroup(groupSize);
VBox paneSize = new VBox(lblSize, rdoSmall, rdoMedium, rdoLarge);
paneSize.setSpacing(10);
// Create the crust pane→72
Label lblCrust = new Label("Crust");
rdoThin = new RadioButton("Thin");
rdoThick = new RadioButton("Thick");
rdoThin.setSelected(true);
ToggleGroup groupCrust = new ToggleGroup();
rdoThin.setToggleGroup(groupCrust);
rdoThick.setToggleGroup(groupCrust);
VBox paneCrust = new VBox(lblCrust, rdoThin, rdoThick);
paneCrust.setSpacing(10);
// Create the toppings pane→84
Label lblToppings = new Label("Toppings");
chkPepperoni = new CheckBox("Pepperoni");
chkMushrooms = new CheckBox("Mushrooms");
chkAnchovies = new CheckBox("Anchovies");
VBox paneToppings = new VBox(lblToppings, chkPepperoni,
chkMushrooms, chkAnchovies);
paneToppings.setSpacing(10);
// Create the buttons→94
Button btnOK = new Button("OK");
btnOK.setPrefWidth(80);
btnOK.setOnAction(e -> btnOK_Click() );
Button btnCancel = new Button("Cancel");
btnCancel.setPrefWidth(80);
btnCancel.setOnAction(e -> btnCancel_Click() );
HBox paneButtons = new HBox(10, btnOK, btnCancel);
// Create the GridPane layout→105
GridPane grid = new GridPane();
grid.setPadding(new Insets(10));
grid.setHgap(10);
grid.setVgap(10);
grid.setMinWidth(500);
grid.setPrefWidth(500);
grid.setMaxWidth(800);
// Add the nodes to the pane→114
grid.addRow(0, lblName, txtName);
grid.addRow(1, lblPhone, txtPhone);
grid.addRow(2, lblAddress, txtAddress);
grid.addRow(3, paneSize, paneCrust, paneToppings);
grid.add(paneButtons,2,4);
// Set alignments and spanning→121
GridPane.setHalignment(lblName, HPos.RIGHT);
GridPane.setHalignment(lblPhone, HPos.RIGHT);
GridPane.setHalignment(lblAddress, HPos.RIGHT);
GridPane.setColumnSpan(txtName,2);
GridPane.setColumnSpan(txtPhone,2);
GridPane.setColumnSpan(txtAddress,2);
// Set column widths→129
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(33);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(33);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(33);
grid.getColumnConstraints().addAll(col1, col2, col3);
// Create the scene and the stage→138
Scene scene = new Scene(grid);
primaryStage.setScene(scene);
primaryStage.setTitle("Pizza Order");
primaryStage.setMinWidth(500);
primaryStage.setMaxWidth(900);
primaryStage.show();
}
public void btnOK_Click()→148
{
// Create a message string with the customer information
String msg = "Customer:\n\n";
msg += "\t" + txtName.getText() + "\n";
msg += "\t" + txtPhone.getText() + "\n\n";
msg += "\t" + txtAddress.getText() + "\n";
msg += "You have ordered a ";
// Add the pizza size
if (rdoSmall.isSelected())
msg += "small ";
if (rdoMedium.isSelected())
msg += "medium ";
if (rdoLarge.isSelected())
msg += "large ";
// Add the crust style
if (rdoThin.isSelected())
msg += "thin crust pizza with ";
if (rdoThick.isSelected())
msg += "thick crust pizza with ";
// Add the toppings
String toppings = "";
toppings = buildToppings(chkPepperoni, toppings);
toppings = buildToppings(chkMushrooms, toppings);
toppings = buildToppings(chkAnchovies, toppings);
if (toppings.equals(""))
msg += "no toppings.";
else
msg += "the following toppings:\n"
+ toppings;
// Display the message
Alert a = new Alert(Alert.AlertType.INFORMATION, msg);
a.setTitle("Order Details");
a.showAndWait();
}
public String buildToppings(CheckBox chk, String msg)→189
{
// Helper method for displaying the list of toppings
if (chk.isSelected())
{
if (!msg.equals(""))
{
msg += ", ";
}
msg += chk.getText();
}
return msg;
}
public void btnCancel_Click()→203
{
stage.close();
}
}
The following paragraphs point out the highlights of this program:
VBox
named paneSize
.VBox
named paneCrust
.VBox
named paneToppings
.HBox
named paneButtons
.VBox
panes are added to row 3. Finally, the HBox
that contains the buttons are added to column 2 of row 4. (Remember: Row and column indexes are numbered from 0, not from 1.)btnOK_Click
method is called when the user clicks OK. This method creates a summary of the customer’s order and displays it using the Alert class.buildToppings
is simply a helper method that assists in the construction of the message string.