Skip to content

Latest commit

 

History

History
623 lines (439 loc) · 37.7 KB

Chapter_7.adoc

File metadata and controls

623 lines (439 loc) · 37.7 KB

Introduction to GUI with JavaFX

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":

fig 7 01
Figure 1. The Calculator

Introduction to JavaFX

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.

fig 7 02
Figure 2. The newly generated IDEA project

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:

fig 7 03
Figure 3. Running the generated application

Brief Overview of JavaFX Layouts

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:

  1. Create instances of child nodes to be used within the layout container.

  2. 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.

  3. Add the child nodes to the container using either the method add() or addAll().

  4. If this layout instance needs to be used inside another layout (e.g. an HBox can be placed inside the BorderPane) add the instance created in Step 1 to the parent container by using the method add().

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.

Getting Started With Scene Builder

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:

fig 7 04
Figure 4. Scene Builder with opened sample.fxml

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:

fig 7 05
Figure 5. Adding a button to the scene

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:

fig 7 06
Figure 6. Scene Builder: previewing in window

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.

GridPane Layout

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.

Designing a Sign In Window in Scene Builder

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:

fig 7 07
Figure 7. The Sign In Window

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:

fig 7 08
Figure 8. A GridPane (2,3)

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.

fig 7 09
Figure 9. A GridPane with nodes in Scene Builder

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:

fig 7 10
Figure 10. Previwing in Scene Builder

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:

fig 7 11
Figure 11. Streching the preview window in Scene Builder

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.

  1. On the left panel of Scene Builder select the GridPane and on the right panel change alignment to be TOP_LEFT.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

Programming the Sign In Window 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.

Styling With CSS

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:

fig 7 12
Figure 12. 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.

BorderPane and Combining Layouts

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:

fig 7 13
Figure 13. An Empty BorderPane

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.

fig 7 14
Figure 14. Adding a VBox for navigation

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".

fig 7 15
Figure 15. Adding buttons to VBox

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:

fig 7 16
Figure 16. Adding a footer with links

Select the menu Preview | Show Preview in Window will help you to see how the window will look during the runtime:

fig 7 17
Figure 17. Previewing the window

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

Project: Creating a GUI for Calculator

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.

fig 7 18
Figure 18. The 4x6 GridPane in the center

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:

fig 7 19
Figure 19. The grid with one button

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:

fig 7 20
Figure 20. Replicated buttons

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.