Using Layout Containers
- Skill Level Intermediate
- Product JavaFX
- Key Features Layouts, HBox, VBox, Tile, Panel, Flow, Stack, Containers
- Last Updated September 2009
In JavaFX SDK 1.2, new layout containers have been incorporated into the programming model for the JavaFX scene graph. These layouts provide flexible ways to arrange the elements of a scene's content. This article discusses the layout mechanisms supported in JavaFX SDK 1.2, considers resizing components, and suggests tips for improving the dynamic layout behavior of your application.
The layout mechanisms available in the JavaFX SDK 1.2 enable you to easily arrange components within a graphical scene. Although you might find absolute positioning more convenient for some applications, using layout containers provides a certain universality and helps you to avoid recalculating coordinates each time the graphical content changes. Consider how to use the container classes to achieve rapid scene layouts.
Concrete Containers
Consider using the concrete layout container classes of the javafx.scene.layout package:
HBox- Arranges its content nodes in a single horizontal column.VBox- Arranges its content nodes in a single vertical column.Flow- Arranges its content nodes in either a horizontal or vertical flow, wrapping at its width (for horizontal) or height (for vertical).Stack- Arranges its content nodes in a back-to-front stack.Tile- Arranges its content nodes in uniformly sized layout spaces or "tiles."
These containers manage nodes added to their content instance variable and lay out both visible and invisible nodes by setting the layoutX and layoutY instance variables to position them.
To gain a better understanding of the concrete containers, look at a layout example and explore it in action. To appreciate the differences between the layouts, switch between the layout models.
VBox and HBox Classes
These classes arrange nodes in a horizontal row or vertical column. Use the spacing variable to set an offset between the nodes. The following instance variables help you to arrange nodes within these containers.
| VBox | HBox |
|---|---|
hpos – Sets the horizontal alignment of the rows within this container's width. Use the HPos class to specify the values. |
|
vpos – Sets the vertical alignment of the columns within this container's height. Use the VPos class to specify the values. |
|
nodeHpos – Sets the horizontal alignment of each node within the vbox's columns. Use the HPos class to specify the values. |
nodeVpos – Sets the vertical alignment of each node within the hbox's row. Use the VPos class to specify the values. |
VBox of five images, sets the spacing interval between them, centers images in the column, and places the column in the center of the container.
def vbox = VBox{
nodeHPos: HPos.CENTER // centers nodes horizontally within the column
hpos: HPos.CENTER // column will be centered within vbox width
spacing: 15 //set an interval between the elements
content: [ for (i in [0..4]) ImageView {image: images[i]} ]
}
Figure 1: Layouts: VBox
def hbox = HBox{
vpos: VPos.CENTER // centers the row vertically within the container
spacing: 10 // sets the an interval between the elements
content: [ for (i in [0..8]) ImageView {image: images[i]} ]
}
Figure 2: Layouts: HBox
Stack Class
This class provides an easy way to arrange nodes in z-order creating the back-to-front layers. The first element declared in the content sequence is located at the bottom of the stack, and the last element is located at the top. The preferred size of a stack is the size required to accommodate the largest preferred width and height of its content.
def stack = Stack{
content: [ for (i in [0..7]) ImageView {image: images[i]} ]
}
Figure 3: Layouts: Stack
Flow Class
The Flow class creates horizontal rows or vertical columns that wrap elements at its width and height boundaries. You can use the vgap and hgap variables to set gaps between rows and between nodes in a row. The hpos and vpos variables enable you configure the alignment of the rows or columns within the flow's width or height.
Note that the Flow layout in the following code fragment could not fit all 16 images into one row. It moves seven elements to the next line and aligns them accordingly. All the images are aligned to the top of the rows.
def flow = Flow{
hgap: 5 // horizontal gap between nodes in a row
vgap: 10 // vertical gap between rows
hpos: HPos.CENTER //column will be centered within vbox width
vpos: VPos.CENTER //centers the row vertically within the container
nodeVPos: VPos.TOP // images are aligned to the top of a row
content: [ for (i in [0..15]) ImageView {image: images[i]} ]
}
Figure 4: Layouts: Flow
Tile Class
This class arranges nodes within a grid of uniform-sized tiles. The number of tiles is defined by the rows and columns variables. The Tile size defaults to the size needed to encompass the largest preferred width and height of its content nodes. You can alter the tile size by specifying the tileWidth and tileHeight variables. Configurable spacing and alignment are supported. The orientation of the tile layout can be either horizontal (the default) or vertical.
The following code fragment creates a grid of 4x4, sets the gaps between the elements, sets the width of the tile, and centers the container.
def tile = Tile{
columns: 4 // creates a grid of cells
rows: 4 // 4x4
tileWidth: 100 // sets the width of the tile
hgap: 5 //horizontal gap between tiles in a row
vgap: 5 // vertical gap between rows of tiles
vertical: true //sets the preferred direction
hpos: HPos.CENTER //column will be centered within vbox width
content: [ for (i in [0..15]) ImageView {image: images[i]} ]
}
Figure 5: Layouts: Tile
The concrete containers are particularly helpful when your layout task matches one of the predefined layout models. If you need to implement a layout different from the models mentioned previously, consider creating a custom layout.
Customizing a Layout
The Panel class of the javafx.scene.layout package can be configured as an object literal to create a customized layout. It is recommended that you use this class instead of the Group class when you need to perform layout of the content. The onLayout variable is a function that is called by the scene graph during the layout pass to perform the layout of the panel's content.
def p: Panel = Panel {
onLayout: function() {
for (n in Container.getManaged(p.content)) {
// position/resize nodes
}
}
onLayout function is used to set the position for the images.
import javafx.scene.layout.Container.*;
def PanelViews = [ for (i in [0..15]) ImageView {image: images[i]} ];
def panel: Panel = Panel{
content: PanelViews
onLayout: function() {
for (node in getManaged(panel.content)) {
def i = indexof node;
positionNode(node, i*40, i*10);
}
}
}
This code fragment produces the following layout.
Figure 6: Layouts: Panel
Resizing Components Within Containers
The LayoutInfo class defines a set of common layout variables that can be used to specify how a particular node should be laid out within the layout space allocated to it by its parent container.
All of the container classes provided in javafx.scene.layout support LayoutInfo. However, setting layoutInfo on a node that is inside one of these containers is required only when the application needs to override the default layout behavior of that container. An example is to override the preferred sizes of a button so that it is always 100 wide in an HBox container.
HBox {
content: [
Button {
layoutInfo: LayoutInfo {
minWidth: 100
width: 100
maxWidth: 100
}
}
// ..other nodes in hbox
]
}
Improving the Dynamic Behavior of Your Application
The layout containers automatically manage the dynamic layout behavior of their content nodes. However, it is often desirable to also use the binding mechanism of JavaFX Script to establish certain dynamic aspects of layout. For example, you can bind the width and height of an HBox container to the scene height and width so that whenever Scene resizes, the HBox container resizes as well.
Stage {
var scene: Scene;
scene: scene = Scene {
width: 300
height: 300
content: HBox {
width: bind scene.width
height: bind scene.height
}
}
Another example of using the binding mechanism is centering a container on the screen. The following code fragment keeps the position of the stack in sync with the size of the scene.
Stage {
var scene: Scene;
var stack: Stack;
scene: scene = Scene {
width: 300
height: 300
content: stack = Stack {
layoutX: bind (scene.width - stack.layoutBounds.width)/2 -
stack.layoutBounds.minX
layoutY: bind (scene.height - stack.layoutBounds.height)/2 -
stack.layoutBounds.minY
content: [...]
}
}
Related Links
We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of repliesyour information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.
Alla Redko
Technical Writer, Sun Microsystems
Inyoung Cho
Java Technology Consultant, Sun Microsystems