After creating lots of code samples producing outputs to the system console, it’s time for creating a nice looking Graphic User Interface (GUI). We’re accustomed to programs that open windows with buttons, text fields, menus and other GUI components.
Java has an older library of GUI components called Swing, and the new one called JavaFX. We’ll use only JavaFX in this book. JavaFX is also a library that’s included with JDK, and you can write your GUI programs using familiar Java language. On the other hand, JavaFX allows you to create and arrange GUI components in a special FXML format, while writing application logic in Java.
In this chapter I’ll explain the basics of GUI programming with JavaFX. You’ll learn how to create GUI containers and arrange GUI components inside them. In the next chapter you’ll learn how to write code that reacts on user’s actions (a.k.a. events) like a click of a button or a mouse move.
By the end of this chapter you should be ready to work on the following assignment: "Create the GUI for a calculator that will look similar to this one":
With JavaFX you can create Graphic User Interface in two ways:
-
By writing the code in Java as you did in previous chapters.
-
With the help of the visual layout tool called Scene Builder, where you’ll be dragging and dropping components on the window and saving the design in the FXML format in a file with extension .fxml. Then the Java program will load this .fxml file and display your creation.
I’ll show you both ways, but let’s start with some basics of development with JavaFX. Let’s set the stage - JavaFX uses theater terminology.
IntelliJ IDEA will help you to get started quickly. Just create a new project (the menu File | New Project), select JavaFX application as the project type and give it a name. In a second new project will be created with two Java classes Main
and Controller
and a file named sample.fxml as shown below.
The Main
class of a JavaFX application extends Application
and is an entry point of your application. JavaFX classes are located in the packages whose names start with javafx
(e.g. javafx.stage
and javafx.scene
).The main
method invokes the method launch
from Application
, which creates a stage by calling its method start
, which was was declared as abstract
. Hence, we must override start
in our Main
class. When the method start
is invoked, it’ll receive a reference to the Stage
object - this is where your GUI components will live and perform.
Pretend that you’re a play director and need to set up one or more scenes (views) on stage. But if in a real scene you’d expect to see some chairs, tables and other pieces of furniture, each JavaFX scene consists of GUI containers and components. In JavaFX you’ll create components, add them to the scene and then add a scene to the stage. When the stage is ready, open the curtain by calling the method show
.
All GUI components are subclasses of the class javafx.scene.Node
, and when I’ll be using the word node just think of a GUI component. Now you have a choice: create these nodes using Java classes or load the pre-created design from the .fxml
file as it was done in the code generated by IDEA.
The method start
loads the file sample.fxml, sets the title of the stage and creates a Scene
300 by 275 pixels.The content of the generated sample.fxml looks like this:
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
</GridPane>
FXML is a markup language based on XML (a popular data presentation format). The idea is to separate the creation of the GUI in a declarative language (FXML) from the application logic (Java). If we didn’t use IDEA, the FXML would be generated by the Scene Builder itself. In FXML the GUI is represented by tags in angle brackets, e.g. <GridPane>
. Each tag has a matching closing tag that starts with a forward slash, e.g. </GridPane>
and may have attributes (e.g. alignment="center"
). The <GridPane>
tag corresponds to the Java class GrindPane
, which is one of the JavaFX layouts - I’ll explain them very shortly. Every FXML tag has a corresponding Java class behind it.
The generated Java class Controller
has just the pair of curly braces at this point, but in the next chapter we’ll be writing the application logic there. Later on you’ll connect the generated FXML views with the Java code of the Controller
. Run the Main
program and it’ll show an empty window shown next:
Users of your application can have computer monitors of different screen sizes and resolutions. JavaFX comes with layouts that help with arranging GUI controls on the scene and keeping the arrangement for different stage sizes (think window sizes). There are following layout container Java classes (and similarly named FXML tags):
-
HBox
-
VBox
-
FlowPane
-
GridPane
-
BorderPane
-
StackPane
-
TilePane
-
AnchorPane
The HBox
class (the horizontal box) places controls in a row next to each other. The vertical box VBox
places controls vertically. The FlowPane
places the nodes in a row and then wraps to the next row if there is no room left in the current one.
The GridPane
allows to arrange UI controls in rows and columns and assign constraints (properties) to each cell.
The BorderPane
splits the scene into five regions, which are called left
, top
, right
, bottom
, and center
.
The TilePane
layout is similar to the FlowPane
, but it places each GUI components in a cell of the same size similarly to tiles on the wall. Say, you need to display several images in a tile layout. Adding the ImageView
components to the TilePane
will shows them as tiles next to each other.
The StackPane
layout works as a deck of playing cards - only one child node will be shown at a time covering the other nodes.
The AnchorPane
lets you to anchor nodes at left, top, right, and bottom of the scene. Imagine a music player application with the buttons Rewind on the left and the Fast Forward on the right. For example, the AncorPane
allows you to force the Rewind button to always remain at 20 pixels from the left border of the window and 10 pixels from the bottom even when the window resizes. For that you can use the leftAnchor
and bottomAnchor
properties. For example,
AnchorPane.setLeftAnchor(rewindBtn, 20);
AnchorPane.setBottomAnchor(rewindBtn, 10);
Components that are located in a container are considered children of this container. Accordingly, a container is called a parent of these components. If you’d be writing the code manually in Java, you’d be going through the following steps with any layout:
-
Create instances of child nodes to be used within the layout container.
-
Create an instance of the selected layout class (e.g.
HBox
). This instance will serve as a container and a layout manager for all child nodes that you’ll add to it. -
Add the child nodes to the container using either the method
add()
oraddAll()
. -
If this layout instance needs to be used inside another layout (e.g. an
HBox
can be placed inside theBorderPane
) add the instance created in Step 1 to the parent container by using the methodadd()
.
In this chapter we’ll use only some of the JavaFX layouts, but for detailed coverage of layouts visit Oracle tutorial at http://docs.oracle.com/javafx/2/layout/builtin_layouts.htm.
It’s easier to create layouts and components using a visual tool, and this is what we’ll do next.
Scene Builder 2.0 is a visual layout tool for JavaFX applications by Oracle. Download it from http://goo.gl/9jOse6. Follow the installation instructions for your operational system and install Scene Builder on your computer. In this section I’ll show you how to quickly get started with Scene Builder, but you should also watch this helpful Youtube video.
You can start the Scene Builder either independently or from IDEA. For example, if you right-click on the sample.fxml file in your newly generated IDEA project it’ll show you a popup menu, which includes the item Open in SceneBuilder. Select this item. The very first time IDEA will ask you to confirm the location of Scene Builder application on your computer. Then it’ll open sample.fxml in Scene Builder. This is how it looks on my computer:
On the left panel you can select containers, controls, menus, shapes and drag and drop them onto the central canvas area. Note the GridPanel layout shown at the bottom left - the arrangement of GUI components inside of this scene will be controlled by GridLayout
. Let me select the Button from the Controls section on the left and drop it on the canvas in the middle. The Screen Builder’s window will look like this:
The right panel allows you to change the properties of this button. These little boxes with the digit one represent so called row and column constraints - we’ll discuss them shortly.
The Scene Builder’s menu Preview | Show Preview in Window will show how your GUI will look during the runtime. So far our one-button screen is not too fancy, and this is how it’s preview looks like:
Let’s save the changes in sample.fxml (menu File | Save) and open this file in the IDEA text editor. When IDEA generated sample.fxml there was no content between <GridPanel>
and `</GridPanel>`in the generated sample.fxml, but now there is:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<children>
<Button mnemonicParsing="false" text="Button"
GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
I’ll go through the details of the GridPane
layout in the next section, but please note that Scene Builder created some tags to specify the constraints for the rows and columns of the grid. The Button
component is placed inside the grid in the cell located in the intersection of the column 1 and row 1.
I’m not going to cover each JavaFX layout in detail, but will show you how to use a pretty powerful layout - GridPane
. When we’ll work on the GUI for the calculator, I’ll also show you how to design a scene using a combination of layouts.
GridPane
divides the area into rows and columns and places GUI components (the nodes) into the grid cells. With GridPane
layout cells don’t have to have the same size - a node can span (be as wide or as tall as several others). If the screen size changes, the content won’t be rearranged and will maintain the grid look.
Before placing the node into a particular cell you have to specify the grid constraints such as rowIndex
and columnIndex
(the coordinate of the cell, which starts with 0,0). The rowSpans
and columnSpan
allow you to make the cell as wide (or as tall) as several other cells. The GridPane
documentation describes lots of various constraints that can define the behavior of each cell’s content if the windows gets resized. I’ll show you a basic example that uses some of these constraints.
I want to create a Sign In window where the user can enter the id, password and press the button Sign In. The scene will use GridPane
layout. The first row will contain a Label
and TextField
for user id, the second row will have a similar pair for the password, and the third row of the grid will have one Button
Sign In that should span two columns. This is how this window should look like:
I’ll start with creating a new IDEA JavaFX project (menus File | New Project | JavaFX Application) giving it the name Signin
. The project with classes Main
, Controller
and the file sample.fxml will be generated. Let’s rename this FXML file into signin.fxml. IDEA will automatically change the corresponding line in the Main
class to load this file instead of sample.fxml:
Parent root = FXMLLoader.load(getClass().getResource("signin.fxml"));
Rename the package from sample to signin (right-click menu, Refactor | Rename). Now open the file signin.fxml in Scene Builder and start thinking about laying out the components of our Sign In scene. Let’s take another look at the image of the Sign In window. I can clearly see that GUI components are placed in three rows. The first two have a Label
and TextField
and the third one has a wide Button
.
I can also recognize two columns in the layout of the Sign In window. The first column has two Label
components and the left side of the Button
. The second column has two TextFiled
components and the right side of the Button
. We can also say that the Button
spans two columns. We’ve got a 2x3 grid!
Open the generated signin.fxml
in Scene Builder. Since this file has already empty <GrigPane>
tag, you’ll see GridPane(0,0) as the root of the hierarchy in the bottom left corner as in Figure 4 above. So far this grid has zero rows and zero columns. Right-click on the grid in the middle of the screen and add three rows and two columns to the grid by using the right-click menus GridPane | Add Row Below and Add Column After. When the GridPane is selected The Scene Builder’s window may look similar to this:
Now let’s drag two Label
controls and a Button
from the Controls section on the left and drop them into the appropriate cells in the first grid column. Change the text on these components to be UserID:, Password, and Sign In.
Then we could drag and drop two TextField
objects in the top two cells in the second column. Actually, it’s not a good idea to enter password in the clear text. I’ll use the TextField
for the user ID, and the PasswordField
(it marks user’s input) for password.
Note that the hierarchy of nodes is shown in the bottom left panel of Scene Builder. If you want to change the properties of a component on a scene with a complex GUI layout it might be easier to select the GUI control in the Hierarchy panel than in the design area in the middle. Now select the menu Preview | Show Preview in Window and you’ll see the following window:
This window doesn’t look exactly as we wanted: there are some issues with alignments, there is no spacing between the container and components, and the button Sign In doesn’t span. But on the bright side, the GridPane
controls the layout and if you’ll try to stretch this window, GUI components won’t change their relative positioning:
Let’s do a couple of more property changes before we’ll run this application from IDEA. In Scene Builder the Properties panel is located on the right and has three sections: Properties, Layout, and Code, and you’ll find the properties to be changed in one of these sections.
-
On the left panel of Scene Builder select the
GridPane
and on the right panel change alignment to beTOP_LEFT
. -
Enter 10 for the padding on top, right, bottom and left for the
GridPane
. We need some spacing between the borders of the scene and the grid. -
Select the
Button
on the left and then change the column span to be 2 and the preferred width to be a large number, say 300. This will make the button wide. -
Select the first column of the grid row by clicking on the little 0 on top of the grid. Set the both preferred and maximum width for this column to be 70.
-
Select the second column of the grid row by clicking on the little 1 on top of the grid. Set the both preferred and maximum width for this column to be 100.
After you do all these changes and save them, the file signin.fxml
will look like this:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="signin.Controller">
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" />
<RowConstraints minHeight="10.0" prefHeight="30.0" />
<RowConstraints minHeight="10.0" prefHeight="30.0" />
</rowConstraints>
<columnConstraints>
<ColumnConstraints maxWidth="70.0" minWidth="10.0" prefWidth="70.0" />
<ColumnConstraints maxWidth="100.0" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<children>
<Label alignment="CENTER" text="User ID:" />
<Label text="Password:" GridPane.rowIndex="1" />
<Button mnemonicParsing="false" prefWidth="300.0" text="Sign In" GridPane.columnSpan="2" GridPane.rowIndex="2" />
<TextField GridPane.columnIndex="1" />
<PasswordField GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</GridPane>
In the <GridPane>
section you see <rowConstraints>
and <columnConstraints>
tags defining the the properties of the rows and columns. The <children>
section contains the declaration of GUI components that the user will see on the screen: <Label>
, <Button>
, <TextField>
, and <PasswordField>
. The <Insets>
section ensures that there is some space between the grid borders and its children.
This is a declarative way of creating GUI in FXML. No Java coding was required to create the GUI for this application.
Finally, let’s set the size of the stage so it can accommodate all components from our scene. In IDEA, open the class Main
, and in the code set the size of the scene to be 200x150 pixels.
primaryStage.setScene(new Scene(root, 200, 150));
Run the Main
program and you’ll see the window that looks similar to the one in Figure 7 above. The work that we’ve done in Scene Builder was a little tedious, but it didn’t require any knowledge of Java. This means that this work can be given to a UI designer, while you’ll concentrate on programming the application logic in Java.
Some people like visual design tools, but others don’t. If you prefer to program everything in Java without using Scene Builder and FXML, you can certainly do it. Below is the Java code of the Sign In window that I’ve written purely in Java without using Scene Builder. It’ll produce the same output as in Figure 7.
public class GridPaneSample extends Application {
public void start(Stage primaryStage) {
final int TWO_COLUMN_SPAN = 2;
Label userIdLbl = new Label("User ID:");
TextField userIdTxt = new TextField();
Label userPwdLbl = new Label("Password:");
PasswordField userPwdTxt = new PasswordField();
GridPane root = new GridPane();
root.setVgap(10);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// Using static methods for setting node constraints
GridPane.setConstraints(userIdLbl, 0, 0);
GridPane.setConstraints(userIdTxt, 1, 0);
GridPane.setConstraints(userPwdLbl, 0, 1);
GridPane.setConstraints(userPwdTxt, 1, 1);
root.getChildren().addAll(userIdLbl, userIdTxt,
userPwdLbl, userPwdTxt);
Button signInBtn = new Button ("Sign In");
// Allow the button to be wider overriding preferred width
signInBtn.setPrefWidth(Double.MAX_VALUE);
// using instance method for directly adding the node
root.add(signInBtn,0,2,TWO_COLUMN_SPAN,1);
Scene scene = new Scene(root,250,150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
After all your efforts in Scene Builder, this Java program shouldn’t be difficult for you to understand. As you see, I use classes named similarly to FXML tags. FXML tags can have attributes (e.g. vgap="10" `), and in Java you’d need to call the corresponding setter (e.g. `root.setVgap(10)
). So the choice is yours - FXML or Java. If you have visual personality use FXML, otherwise use Java.
It would be boring if all applications would look the same. Application windows may have different colors, fonts, buttons with rounded corners or use special visual effects. In other words, applications have different styles. Even though you can style JavaFX GUI components programmatically (e.g. by calling methods setFont()
or setFill()
) separating styling from programming allows professional UI designers to take care of the look and feel while software developers implement application logic.
Separating the work of programmers and designers became popular in Web applications. Cascading Style Sheets (CSS) is a special language for styling UI. Styles of GUI components are stored in separate .css files and are loaded and applied to components by the application’s code. Sometimes this process is called skinning - you can create an application that can "wear different skins" changing its look to the user’s liking. JavaFX has a default skin, and if you’re interested in how to create custom skins, visit Oracle online tutorial Skinning JavaFX applications with CSS.
Even if you won’t create your own CSS file, your JavaFX application applies default CSS style to the components of your view. In Scene Builder you can see these styles. Just select any component and then go to the menu View | Show CSS Analyzer. You’ll see a number of styles the start with the prefix fx-
, which makes JavaFX CSS a little different from a standard CSS syntax.
Covering CSS in detail would require a separate book, but I’ll show you a simple example of how the look of the GUI can be changed without the need to modify the Java code.
You can either create so-called CSS selectors to style a specific GUI component, a type of components (e.g. all buttons), or create a reusable style that can be applied programmatically to a selected component.
To style a specific component it has to have a unique id. If you program GUI in Java, set in your Java code using the method setId()
, for example:
Button signInBtn = new Button ("Sign In");
signInBtn.setId("submitBtn");
In FXML just add an id
attribute to the tag of the component:
<Button id="submitBtn" text="Sign In">
For a button with an id submitBtn
you can add the following section to the CSS file to make its background color red:
#submitBtn{
-fx-background-color: red;
}
You can find the names of the main CSS colors online. In CSS the id type selectors start with the #-sign as in #submitBtn
.
If you want to apply a style to several components of the same type, you need to define a type selector. For example, to make the text of all Label
components red, you can define the following CSS type selector:
.label{
-fx-text-fill: red;
}
Note that CSS type selectors start with the dot. To create a CSS class selector that can be applied to any component, define the selector with an arbitrary name and apply it programmatically to the components of your choice. For example, you can specify the class selector .blueLable
:
.bluelabel{
-fx-text-fill: blue;
-fx-font-family:verdana;
-fx-font-style:italic;
}
This class selector defines the rules that will display text of the component in blue bold verdana font in italic style. Typically, you’ll be loading the entire CSS file when the application starts so all styles are available for use. If you use Java for GUI programming, you can apply a class selector to a specific button just like this:
Label userPwdLbl = new Label("Password:");
userPwdLbl.getStyleClass().add("bluelabel");
In FXML assigning a CSS class selector is done by adding the attribute styleClass
to the tag element:
<Label text="Password:" styleClass="bluelabel" GridPane.rowIndex="1" />
You may ask, "How am I supposed to know which style properties are available for a given JavaFX component?" All JavaFX styles are described in the online document titled "JavaFX CSS Reference Guide".
Let’s learn how to apply all these styling techniques to the Sign In window from the previous section. Using IDEA menu File | New create a new file signin.css in Signin project. Then add the following content to it:
#submitBtn{
-fx-background-color: lightskyblue;
-fx-font-family:verdana;
-fx-font-size:20;
-fx-font-weight: bold;
-fx-stroke:navy;
-fx-font-style:italic;
-fx-border-radius: 20;
-fx-background-radius: 20;
-fx-padding: 5;
}
.label{
-fx-text-fill: red;
}
.bluelabel{
-fx-text-fill: blue;
-fx-font-family:verdana;
-fx-font-style:italic;
}
The file signin.css defines three styles:
-
an id selector for the component with the id
submitBtn
-
a type selector for all
Label
components -
a class selector
bluelabel
that can be applied to certain labels.
To apply this CSS file to our Sign In application add the attribute id="submitBtn"
to the <Button>
element in signin.fxml.
Then add the attribute styleClass="bluelabel"
to the <Password>
tag in signin.fxml
.
Finally, in Main.java
load the signin.css
and apply it to the scene. The new version of Main.java
will look like this:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("signin.fxml"));
primaryStage.setTitle("Sign In");
Scene scene = new Scene(root, 200, 150);
scene.getStylesheets().add(getClass()
.getResource("signin.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Run the Main
application and you’ll see a differently styled Sign In window:
When the application starts loading our CSS file it sees that all labels must be red because of the type selector for labels. But then the application notices a more specific style for the Label
Password: styleClass="bluelabel"
, so it paints the text Password in blue.
Pretty often you see applications that split the window into several distinct areas - the header goes on top, the navigation bar is on the left (or right), the footer’s at the bottom of the page and a large content area occupies the middle portion of the window. The BorderPane
layout allows you to do exactly this - split the scene into up to five regions called left
, top
, right
, bottom
, and center
.
Each of these regions can contain either a single GUI component or a container that will have "children" - components allocated in their own container, e.g. inside a GridPane
. You’ll use BorderPane
layout with a nested GridPane
while working on the calculator following instructions from the Project section at the end of this chapter. Meanwhile, let’s create a very basic window illustrating how the BorderPane
layout works.
Open Scene Builder and create a new FXML file by selecting the menu File | New. Then drag the BorderPane
from the left and drop in the middle. Click on the BorderPane
and you’ll see a screen that can look as follows:
Now select Insert TOP at the bottom left, and then drag and drop a Label
from the Controls section onto the middle section of Screen Builder. Set the label’s title to "This is the Header". This text will be displayed at the top.
Select Insert LEFT and then drag and drop VBox
from the Containers area to the left side of the middle section of the Screen Builder. The VBox
is a container for arranging GUI components vertically, which is what we need for creating a navigation menu. You’ll see an empty gray area on the left.
Now select VBOX
at the bottom left and add a couple of menu items to the empty VBox
. Drag a Button
from the Controls section and drop it onto the VBox
. Change its text to read "Menu 1", and make it a little wider so it fits the VBox
nicely. Add two more buttons labeled "Menu 2" and "Menu 3".
Our left navigation bar has a menu that’s implemented as buttons. In Chapter 10 while developing a Tic-Tac-Toe game I’ll show you how to create real menus.
Let’s add a footer to the window now. Select Insert BOTTOM at the bottom left and then drag the HBox
from the Containers area to the middle section. It’ll look like a gray area at the bottom. Adjust its height so the footer doesn’t take too much real estate in our scene. Now add three Hyperlink
components from the Contols section to the footer and set their text to "Link 1", "Link 2", and "Link 3" correspondingly. The Scene Builder’s window should look like this:
Select the menu Preview | Show Preview in Window will help you to see how the window will look during the runtime:
Of course, this window may need more work on styling controls and adjusting sizes and alignments, but as long as you understand how to lay out a scene, they shouldn’t be too difficult. Creating and applying CSS styles can make this windows a lot prettier.
Save the layout in a file border.fxml using Scene Builder’s menu File | Save. While you’ve been dragging and dropping components, Scene builder was working hard generating the corresponding FXML code in border.fxml. If you open this file in any text editor, its content may look similar to this:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label text="This is the Header" BorderPane.alignment="CENTER" />
</top>
<left>
<VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="99.0" text="Menu 1" />
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="99.0" text="Menu 2" />
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="99.0" text="Menu 3" />
</children>
</VBox>
</left>
<bottom>
<HBox prefHeight="42.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<children>
<Hyperlink text="Link 1" />
<Hyperlink text="Link 2" />
<Hyperlink text="Link 3" />
</children>
</HBox>
</bottom>
</BorderPane>
Don’t be overwhelmed with the amount of tags and attributes in the above code. You can identify the regions of the BorderPane
layout. The <top>
region contains a label, while the left
and <bottom>
regions have containers with their own layouts. In this example I have not used the <center>
and <right>
regions. Typically your program will be changing the content of the central area based on the user actions. For example, if the user clicks on the "Menu 1" button in the left region, JavaFX will generate a clicked event and you’ll show the content required for this selection in the central area.
You’ll learn how to process events in the next chapter. Now it’s time to practice in working with combined layouts
Using Scene Builder and a combination of the BorderPane
and GridPane
layout, create GUI for the calculator that looks as seen in Figure 7.1. Add the TextField
to the north
region of the BorderPane
. Then add a GridPane
container to the center
area - you’ll add buttons to this container.
Most of the calculators have a display field on top and the buttons just below it. As you can see on Figure 1, the grid with buttons has four columns and six rows. The default GridPane
contains two columns and three rows. You’ll need to right-click on the grid and use the menus GridPane | Add Row Above and Add Column After to until you see a 4x6 grid as shown below.
Note that I set the Padding
property to be 10 for all sides of the grid and the Hgap
and Vgap
to 5 (the gap between the cells).
Save the layout in the file calculator.fxml. Then drop a Button
into the top left cell of the grid. Set the Margin
property to 5 for each side of this button. This is the distance between the button and cell borders. Drag the button’s border to make it larger. This is what you should see:
If you’re curious how this button looks in the calculator.fxml, open this file in any text editor and note the section <children>
that in my case looks like this:
<children>
<Button mnemonicParsing="false" prefHeight="37.0" prefWidth="132.0" text="Button">
<GridPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</GridPane.margin>
</Button>
</children>
There is no indication of the cell (0,0) because zeros are the default values for GridPane.columnIndex
and GridPane.rowIndex
properties. Now you need to replicate this button in other cells. You can use multiple Ctrl-C/Ctrl-V commands and then drag/drop the buttons into other cells. See how the content of the calculator.fxml changes as you add more buttons. I find it faster to copy/paste the generated code in the FXML file than using Scene Builder for mass duplication.
Note that in Figure 7.1, the button with coordinates (0,5) spans two columns, and the button with coordinates (3,4) spans two rows. For these buttons you’ll need to enter 2 as the row (or column) span, and select MAX_VALUE
as maximum width (or height). This is what you should see by now:
Change the text of each button to look Figure 7.1, and the layout is done.
Then create a new JavaFX project Calculator in IDEA and replace the generated sample.fxml with calculator.fxml created by Scene Builder. Modify the generated class Main
to use calculator.fxml. Rename the package from sample to mycalculator. Set the size of the scene to be large enough to accommodate your design. This is how my class Main
looks like:
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("calculator.fxml"));
primaryStage.setTitle("My JavaFX Calculator");
primaryStage.setScene(new Scene(root,650,600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Run the Main
program from IDEA to ensure that your calculator looks as expected. Don’t allow the users to resize your calculator by invoking the method setResizable
on the stage object.
Then create a CSS file to add some cool styling to the calculator’s buttons. Save the CSS file in your IDEA project and modify the code of the Main
class to use your CSS file similarly to how it was done in the section "Styling With CSS" above. Make your calculator look better than mine - it’s not too difficult.
After completing this assignment your buttons won’t work just yet. In the next chapter you’ll learn how to make the buttons (or other components) to react to user actions, so you’ll be able to complete the calculator. In this chapter my goal was to introduce you to basic rules of designing JavaFX GUI with the help of Scene Builder. Watch this YouTube video to see how easy it is to design more complex views with Scene Builder.