|
The layout mechanism in the JavaFX SDK enables you to easily arrange and align components without specifying absolute coordinates for each UI object. Although absolute coordinates provide a certain flexibility, you might occasionally find the layout mechanism more convenient. The example code provided in this tutorial uses data binding and the declarative syntax of the JavaFX Script programming language. Refer to Learning the JavaFX Script Programming Language for more details on these concepts.
|
The example introduced in this lesson demonstrates how to arrange geometric shapes and standard UI components available through the JavaFX API.
Contents
Consider creating three circles and a toggle group of radio buttons. Each radio button controls a color in the corresponding circle. When the radio button is selected the color is applied, otherwise the circle is gray. This scenario simulates a traffic light and is the example used in this tutorial to describe the layout mechanism.
To create a window:
- Add an
import statement for the Stage and Scene classes.
- Add the
Stage object literal.
- Specify the title of the window.
- Add the
Scene object literal to the scene instance variable of the Stage class.
- Set the width and the height of the scene.
import javafx.stage.Stage;
import javafx.scene.Scene;
Stage {
title: "Traffic lights"
scene: Scene{
width: 210
height: 90
}//Scene
}//Stage
|
Create a group of radio buttons in which only one button can be selected at a given time. This is called a toggle group. To define radio buttons:
- Add
import statements for the ToggleGroup and RadioButton classes.
- Define the toggle group by using the
ToggleGroup class.
Note: In the example code for this lesson, you define a variable name for each UI element so that you can easily learn how the layout mechanism works. You also apply binding to the UI elements by referring to the variable names.
- Define the
choiceText sequence for the button captions.
- Create a sequence of radio buttons within a cycle construction. The buttons enable selection of the particular traffic light by using the
RadioButton class.
- Add each radio button to the group by using the
toggleGroup instance variable.
The following code fragment defines a toggle group, then creates radio buttons that add them to the group.
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.RadioButton;
def group = ToggleGroup{};
def choiceText = ["STOP", "READY", "GO"];
def choices = for (text in choiceText)
RadioButton{
toggleGroup: group
text: text
}//RadioButton
|
To draw circles that indicate the traffic lights:
- Add
import statements for the Circle, Color, RadialGradient, and Stop classes.
- Define the
colors sequence for the colors of the circles.
- Create a sequence of circles within a cycle construction.
- Apply radial gradient fill to visually enhance the example.
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
def group = ToggleGroup{};
def choiceText = ["STOP", "READY", "GO"];
def choices = for (text in choiceText)
RadioButton{
toggleGroup: group
text: text
}//RadioButton
var colors = ["RED", "GOLD", "GREEN"];
var lights = for (color in colors)
Circle {
centerX: 12
centerY: 12
radius: 12
stroke: Color.GRAY
fill: bind RadialGradient {
centerX: 8,
centerY: 8,
radius: 12,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.WHITE},
Stop {offset: 1.0 color:
if (choices[indexof color].selected)
then Color.web(color)
else Color.GRAY
}//Stop
]
}//RadialGradient
}//Circle
|
The preceding code sample uses the data binding mechanism to change the color of the circle. If the selected instance variable of choices[1] is true, the color instance variable becomes Color.RED, otherwise it is Color.GRAY. Refer to Creating Graphical Objects for more information about shapes and visual effects.
After all the components are created, you can use layout containers to arrange elements within the scene. This lesson explores the JavaFX Container: HBox, VBox, and Tile. In this example, you need a VBox object to lay out the radio buttons, an HBox object for the circles, and another HBox to arrange those two boxes.
Figure 4: Combination of HBox and VBox
To lay out the circles horizontally:
- Add an
import statement for the HBox class.
- Arrange the circles within the horizontal box.
- Specify the
spacing variable to set an offset between the circles.
- Set the
nodeVPos instance variable to VPos.CENTER in this container so that circles will be centered vertically.
The nodeVPos variable defines the vertical position of each node within the HBox container. Its default value is VPos.TOP.
- Remember to add an import statement for the
VPos class.
HBox{ spacing: 15 content: lights nodeVPos: VPos.CENTER}
|
To arrange the radio buttons:
- Add an
import statement for the VBox class.
- Add the radio button sequence to the container's content.
- Set the vertical offset between the radio buttons by using the
spacing variable.
VBox{
spacing: 10
content: choices
}
|
- Use another
HBox container to lay out the vertical box with the radio buttons and the horizontal box with the circles.
Note: All the Container classes constrain their children classes to fit a particular size, unlike the Group class that keeps its children size intact.
Hence, if you would like to change a size of the RadioButton added to the VBox, its size is not honored, because the container resizes the node to its preferred size. To change the predefined size of the UI control use the LayoutInfo class as follows:
RadioButton{
toggleGroup: group
text: text
layoutInfo: LayoutInfo { width: 150 }
}//RadioButton
|
See the complete code of the example as follows.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.ToggleGroup;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.control.RadioButton;
import javafx.geometry.VPos;
def group = ToggleGroup{};
def choiceText = ["STOP", "READY", "GO"];
def colors = ["RED", "GOLD", "GREEN"];
def choices = for (text in choiceText)
RadioButton{
toggleGroup: group
text: text
}//RadioButton
def lights = for (color in colors)
Circle {
centerX: 12
centerY: 12
radius: 12
stroke: Color.DARKGRAY
fill: bind RadialGradient {
centerX: 8,
centerY: 8,
radius: 12,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.WHITE},
Stop {offset: 1.0 color:
if (choices[indexof color].selected)
then Color.web(color)
else Color.GRAY
}//Stop
]
}//RadialGradient
}//Circle
Stage {
title: "Traffic lights"
scene: Scene{
width: 210
height: 90
content:
HBox{
spacing: 20
content:[
VBox{ spacing: 10
content: choices
}
HBox{ spacing: 15 content: lights nodeVPos: VPos.CENTER}
]
}//HBox
} //Scene
}//Stage
|
When compiled and run, this code produces the following window.
Figure 5: Vertical Box With Radio Buttons and Horizontal Box With Circles
Alternatively you can use another layout manager represented by the Tile class to arrange the same components, but in a slightly different order. The Tile layout arranges elements in a grid of cells. Each cell is exactly the same size. The rows and columns instance variables define the grid's size.
Figure 6: Structure of the Tile Layout
To apply the Tile layout:
- Add an import statement for the
Tile, Insets, and HPos classes.
- Add the
Tile object literal to the scene's content.
- Define the number of rows and columns in the grid.
- Use a cycle construction to arrange elements within the grid so that each row contains a radio button and a circle.
- Define the amount of horizontal and vertical space between elements by using the
hgap and vgap variables.
- Specify the top and left padding around the container's contents.
- Specify the width of each tile by using the
tileWidth instance variable
- Set the
nodeHPos instance variable to HPos.LEFT to align the elements left.
The following code fragment shows how to create a Tile object with three rows and two columns.
Tile {
columns: 2
rows: 3
tileWidth: 40
nodeHPos: HPos.LEFT
padding: Insets{top: 10 left: 10}
vgap: 5
hgap: 10
content: for (i in [0..2])
[choices[i], lights[i]]
}//Tile
|
See the complete code of the example as follows.
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.RadioButton;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.layout.Tile;
import javafx.geometry.Insets;
import javafx.geometry.HPos;
def group = ToggleGroup{};
def choiceText = ["STOP", "READY", "GO"];
def colors = ["RED", "GOLD", "GREEN"];
def choices = for (text in choiceText)
RadioButton{
toggleGroup: group
text: text
}//RadioButton
def lights = for (color in colors)
Circle {
centerX: 12
centerY: 12
radius: 12
stroke: Color.DARKGRAY
fill: bind RadialGradient {
centerX: 8,
centerY: 8,
radius: 12,
proportional: false
stops: [
Stop {offset: 0.0 color: Color.WHITE},
Stop {offset: 1.0 color:
if (choices[indexof color].selected)
then Color.web(color)
else Color.GRAY
}//Stop
]
}//RadialGradient
}//Circle
Stage {
title: "Traffic lights"
scene: Scene{
width: 210
height: 100
content:
Tile {
tileWidth: 40
nodeHPos: HPos.LEFT
padding: Insets{top: 10 left: 10}
columns: 2
rows: 3
vgap: 5
hgap: 10
content: for (i in [0..2])
[choices[i], lights[i]]
}//Tile
} //Scene
}//Stage
|
When compiled and run, the modified application produces the following window.
Figure 7: Tile Layout of the Radio Button and Circles
Try to lay out other components, for example, buttons.
Conclusion
In this lesson, you learned how to lay out UI components in a scene. You can use the layout mechanism, absolute coordinates, or a combination of those two approaches for your current task. Refer to the API documentation for more information about layout.
|
Alla Redko is a technical writer at Sun. She develops documentation for Java SE and JavaFX. Prior to her assignment to Sun, she worked as a technical writer for 10 years.
|