Mastering FXML

Previous
Next

4 Creating an Address Book with FXML

In this tutorial, you create an Address Book application that includes a table of names and email addresses, as shown in Figure 4-1. The tutorial shows how to populate a table with data, sort the data at application startup, align the data in the table cells, and add rows to the table.

Some amount of knowledge of FXML and application development is assumed for this tutorial. Before you start, you should have completed the FXML tutorial in the Getting Started series, because it teaches the basics of FXML development. Specifically, for the Address Book tutorial, you should know:

  • The basic structure of an FXML project (.java, .fxml, and controller files)

  • How to create and run a JavaFX FXML project in NetBeans IDE

  • The basics of layout and user interface components

Before you begin this tutorial, ensure that the version of NetBeans IDE that you are using supports your version of JavaFX 2. See the System Requirements for details.

Figure 4-1 Address Book Application

Description of Figure 4-1 follows
Description of "Figure 4-1 Address Book Application"

Set Up the Project

Your first task is to set up a JavaFX FXML project in NetBeans IDE.

  1. From the File menu, choose New Project.

  2. In the JavaFX category, choose JavaFX FXML Application. Click Next.

  3. Name the project FXMLTableView and click Finish.

    NetBeans IDE opens an FXML project that includes the code for a basic Hello World application. The application includes three files: FXMLTableView.java, Sample.fxml, and SampleController.java.

  4. Rename SampleController.java to FXMLTableViewController.java so that the name is more meaningful for this application.

    1. In the Projects window, right-click SampleController.java and choose Refactor then Rename.

    2. Enter FXMLTableViewController, and then click Refactor.

  5. Rename Sample.fxml to fxml_tableview.fxml.

    1. Right-click Sample.fxml and choose Rename.

    2. Enter fxml_tableview and click OK.

  6. Open FXMLTableView.java and edit the FXMLTableView class to look like Example 4-1.

    Example 4-1 FXMLTableView.java

    public class FXMLTableView extends Application {
        
        @Override
        public void start(Stage primaryStage) throws Exception {
           primaryStage.setTitle("FXML TableView Example");
           Pane myPane = (Pane)FXMLLoader.load(getClass().getResource
        ("fxml_tableview.fxml"));
           Scene myScene = new Scene(myPane);
           primaryStage.setScene(myScene);
           primaryStage.show();
        }
     
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    Note that the Java file does not contain the code for the scene. In the next section of the tutorial, Create the Basic User Interface, you will add the code for the scene in the FXML file.

  7. Press Ctrl (or Cmd) + Shift + I to correct the import statements.

Create the Basic User Interface

Define the user interface by creating a GridPane layout container as the root node of the scene. Then, add a Label and a TableView component as child nodes of the GridPane layout container.

  1. Open the fxml_tableview.fxml file.

  2. Delete the <AnchorPane> markup that NetBeans IDE automatically generated.

  3. Add a GridPane layout container as the root node of the scene as shown in Example 4-2.

    Example 4-2 GridPane

    <GridPane alignment="CENTER" hgap="10.0" vgap="10.0"
        xmlns:fx="http://javafx.com/fxml"
        fx:controller="fxmltableview.FXMLTableViewController">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
        </padding> 
    </GridPane>
    

    You can ignore the error "File not found in the specified address: http://javafx.com/fxml" that might appear in the output window.

  4. Add a Label and a TableView component to the GridPane layout container. The code is in Example 4-3.

    Example 4-3 Label and TableView

    <GridPane alignment="CENTER" hgap="10.0" vgap="10.0"
        xmlns:fx="http://javafx.com/fxml"
        fx:controller="fxmltableview.FXMLTableViewController">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font: NORMAL 20 Tahoma;" text="Address Book"                
            GridPane.columnIndex="0" GridPane.rowIndex="0">
        </Label>
        <TableView fx:id="tableView" GridPane.columnIndex="0" 
            GridPane.rowIndex="1">
        </TableView>
    </GridPane>
    
  5. Add the import statement for the Insets class.

    <?import javafx.geometry.Insets?>

  6. Run the program. You will see the label Address Book and a table with the text "No columns in table," which is the default caption defined by the TableView implementation, as shown in Figure 4-2.

    Figure 4-2 Table with No Columns

    Description of Figure 4-2 follows
    Description of "Figure 4-2 Table with No Columns"

Add Columns to the Table

Use the TableColumn class to add three columns for displaying the data: First Name, Last Name, and Email Address. The code is in Example 4-4.

Example 4-4 Table Columns

<TableView fx:id="tableView" GridPane.columnIndex="0" GridPane.rowIndex="1">
     <columns>
          <TableColumn text="First Name">
          </TableColumn>
          <TableColumn text="Last Name">
          </TableColumn>
          <TableColumn text="Email Address">
          </TableColumn>
     </columns>    
</TableView>

Tip: For more information on the TableColumn class or any other JavaFX class discussed in this tutorial, see the API documentation.

Figure 4-3 shows the table with the columns for First Name, Last Name, and Email Address.

Figure 4-3 Address Book with Three Columns

Description of Figure 4-3 follows
Description of "Figure 4-3 Address Book with Three Columns"

Define the Data Model

When you create a table in a JavaFX application, it is a best practice to implement a class that defines the data model and provides methods and fields to further work with the table. Create a Person class to define the data for the address book.

  1. In NetBeans IDE, right-click the fxmltableview folder under Source Packages, and choose New then Java Class.

  2. Name the class Person and then click Finish.

  3. Implement a Person class to define the data, as shown in Example 4-5.

    Example 4-5 Person Class

    package fxmltableview;
     
    import javafx.beans.property.SimpleStringProperty;
     
    public class Person {
       private final SimpleStringProperty firstName = new SimpleStringProperty("");
       private final SimpleStringProperty lastName = new SimpleStringProperty("");
       private final SimpleStringProperty email = new SimpleStringProperty("");
    
    public Person() {
            this("", "", "");
        }
     
        public Person(String firstName, String lastName, String email) {
            setFirstName(firstName);
            setLastName(lastName);
            setEmail(email);
        }
    
        public String getFirstName() {
            return firstName.get();
        }
     
        public void setFirstName(String fName) {
            firstName.set(fName);
        }
            
        public String getLastName() {
            return lastName.get();
        }
        
        public void setLastName(String fName) {
            lastName.set(fName);
        }
        
        public String getEmail() {
            return email.get();
        }
        
        public void setEmail(String fName) {
            email.set(fName);
        }
    }
    

Associate Data with the Table Columns

The next tasks are to define rows for the data and associate the data with the table columns. You add this code to the FXML file.

  1. In the fxml_tableview.fxml file, create an ObservableList array and define as many data rows as you would like to show in your table. Sample code is in Example 4-6. Add the code between the </columns> and </TableView> markup.

    Example 4-6 ObservableList Array

    </columns>
    <items>
        <FXCollections fx:factory="observableArrayList">
            <Person firstName="Jacob" lastName="Smith"  
                 email="jacob.smith@example.com"/>
            <Person firstName="Isabella" lastName="Johnson" 
                 email="isabella.johnson@example.com"/>
            <Person firstName="Ethan" lastName="Williams" 
                 email="ethan.williams@example.com"/>
            <Person firstName="Emma" lastName="Jones"
                 email="emma.jones@example.com"/>
            <Person firstName="Michael" lastName="Brown" 
                 email="michael.brown@example.com"/>
        </FXCollections>
    </items>
    </TableView>
    
  2. Specify a cell factory for each column to associate the data with the column, as shown in Example 4-7.

    Example 4-7 Cell Factories

    <columns>
         <TableColumn text="First Name">
            <cellValueFactory><PropertyValueFactory property="firstName" />
            </cellValueFactory>
         </TableColumn>
         <TableColumn text="Last Name">
            <cellValueFactory><PropertyValueFactory property="lastName" />
            </cellValueFactory>
         </TableColumn>
         <TableColumn text="Email Address">
             <cellValueFactory><PropertyValueFactory property="email" />
             </cellValueFactory>
        </TableColumn>
    </columns> 
    

    Cell factories are implemented by using the PropertyValueFactory class, which uses the firstName, lastName, and email properties of the table columns as references to the corresponding methods of the Person class.

  3. Import the required packages, as shown in Example 4-8:

    Example 4-8 Import Statements

    <?import javafx.scene.control.cell.*?> 
    <?import javafx.collections.*?> 
    <?import fxmltableview.*?> 
    

Running the application at this point shows the table populated with data, as shown in Figure 4-4.

Figure 4-4 Table with Data

Description of Figure 4-4 follows
Description of "Figure 4-4 Table with Data"

Here are some built-in features of the TableView class for you to try:

  • Resize a column width by dragging the column divider in the table header to the left or right.

  • Move a column by dragging the column header.

  • Alter the sort order of data by clicking a column header. The first click enables an ascending sort order, the second click enables a descending sort order, and the third click disables sorting. By default, no sorting is applied.

Set Sort Order on Startup

In this task, you set the sort order so that the entries in the First Name column appear in ascending alphabetical order on application startup. You do this by creating an ID for the table column and then setting up a reference to it.

  1. Add an ID to the First Name column:

    <TableColumn fx:id="firstNameColumn" text="First Name">

  2. Specify the sort order by adding the code in Example 4-9 between the </items> and </TableView> markup.

    Example 4-9 Sort Order

         </items>
         <sortOrder>
              <fx:reference source="firstNameColumn"/>
         </sortOrder> 
    </TableView>
    

You can see the results in Figure 4-5.

Figure 4-5 Table with First Column Data Sorted at Startup

Description of Figure 4-5 follows
Description of "Figure 4-5 Table with First Column Data Sorted at Startup"

Define Column Widths

Add the prefWidth property to increase the column widths, as shown in Example 4-10.

Example 4-10 Column Widths

     <TableColumn fx:id="firstnameColumn" text="First Name" prefWidth="100">
        <cellValueFactory><PropertyValueFactory property="firstName" />
        </cellValueFactory>
     </TableColumn>
     <TableColumn text="Last Name" prefWidth="100">
        <cellValueFactory><PropertyValueFactory property="lastName" />
        </cellValueFactory>
     </TableColumn>
     <TableColumn text="Email Address" prefWidth="200">
        <cellValueFactory><PropertyValueFactory property="email" />
        </cellValueFactory>
     </TableColumn>

The result is in Figure 4-6. The column widths have been increased so that all data is visible in each table row.

Figure 4-6 Table with Column Widths Set

Description of Figure 4-6 follows
Description of "Figure 4-6 Table with Column Widths Set"

Set Alignment in Table Cells

Another customization is to set the alignment of the data in the table cells. You implement the logic in a new class named FormattedTableCellFactory and then set the alignment in the <TableColumn> markup in the FXML code.

  1. In NetBeans IDE, right-click the fxmltableview folder under Source Packages, and choose New then Java Class.

  2. Name the class FormattedTableCellFactory and then click Finish.

  3. Modify the FormattedTableCellFactory class by implementing the Callback class and creating instances of the TextAlignment and Format classes, as shown in Example 4-11. The S parameter is the type of the TableView generic type and the T parameter is the type of the content of all cells in this table column.

    Example 4-11 Callback Class

    public class FormattedTableCellFactory<S, T> 
        implements Callback<TableColumn<S, T>, TableCell<S, T>> {
        private TextAlignment alignment;
        private Format format;
     
        public TextAlignment getAlignment() {
            return alignment;
        }
     
        public void setAlignment(TextAlignment alignment) {
            this.alignment = alignment;
        }
     
        public Format getFormat() {
            return format;
        }
     
        public void setFormat(Format format) {
            this.format = format;
        }
    
  4. Implement the TableCell and TableColumn classes by appending the code in Example 4-12. This code overrides the updateItem method of the TableCell class and calls the setTextAlignment method on the table cell.

    Example 4-12 TableCell and TableColumn Classes

    @Override
        @SuppressWarnings("unchecked")
        public TableCell<S, T> call(TableColumn<S, T> p) {
            TableCell<S, T> cell = new TableCell<S, T>() {
                @Override
                public void updateItem(Object item, boolean empty) {
                    if (item == getItem()) {
                        return;
                    }
                    super.updateItem((T) item, empty);
                    if (item == null) {
                        super.setText(null);
                        super.setGraphic(null);
                    } else if (format != null) {
                        super.setText(format.format(item));
                    } else if (item instanceof Node) {
                        super.setText(null);
                        super.setGraphic((Node) item);
                    } else {
                        super.setText(item.toString());
                        super.setGraphic(null);
                    }
                }
            };
            cell.setTextAlignment(alignment);
            switch (alignment) {
                case CENTER:
                    cell.setAlignment(Pos.CENTER);
                    break;
                case RIGHT:
                    cell.setAlignment(Pos.CENTER_RIGHT);
                    break;
                default:
                    cell.setAlignment(Pos.CENTER_LEFT);
                    break;
            }
            return cell;
        }
    }
    
  5. Correct the import statements.

  6. In the fxml_tableview.fxml file, add the following code under the <cellValueFactory> markup to provide a center alignment for the First Name column, as shown in Example 4-13.

    Example 4-13 Alignment in Data Cell

    <TableColumn fx:id="firstNameColumn" text="First Name" prefWidth="100">
         <cellValueFactory><PropertyValueFactory property="firstName" />
         </cellValueFactory>
         <cellFactory>
              <FormattedTableCellFactory alignment="center">
              </FormattedTableCellFactory>
         </cellFactory>
    </TableColumn>
    

    You can create an alignment for the remaining columns using left, right, or center values.

Running the application now results in data that is aligned in the center of the First Name column, as shown in Figure 4-7.

Figure 4-7 Data Center-Aligned in First Name Column

Description of Figure 4-7 follows
Description of "Figure 4-7 Data Center-Aligned in First Name Column"

Add Rows to the Table

You can add the ability for users to add a row of data to the table. Add the application logic in the FXMLTableViewController class. Then, modify the user interface to include three text fields and a button for entering the data.

  1. Open the FXMLTableViewController.java file.

  2. Edit the FXMLTableViewController class so it looks like the code in Example 4-14.

    Example 4-14 FXMLTableViewController.java

    public class FXMLTableViewController {
        @FXML private TableView<Person> tableView;
        @FXML private TextField firstNameField;
        @FXML private TextField lastNameField;
        @FXML private TextField emailField;
        
        @FXML
        protected void addPerson(ActionEvent event) {
            ObservableList<Person> data = tableView.getItems();
            data.add(new Person(firstNameField.getText(),
                lastNameField.getText(),
                emailField.getText()
            ));
            
            firstNameField.setText("");
            lastNameField.setText("");
            emailField.setText("");   
        }
    }
    
  3. Correct the import statements, as shown in Example 4-15.

    Example 4-15 Import Statements in FXMLTableViewController

    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.scene.control.TableView;
    import javafx.scene.control.TextField;
    
  4. In the fxml_tableview.fxml file, add the following code before the </GridPane> markup, as shown in Example 4-16.

    Example 4-16 Text Fields and Button for Adding a Row

         </TableView>
         <HBox spacing="10" alignment="bottom_right" GridPane.columnIndex="0" 
              GridPane.rowIndex="2">
              <TextField fx:id="firstNameField" promptText="First Name"
                   prefWidth="90"/>
              <TextField fx:id="lastNameField" promptText="Last Name"
                   prefWidth="90"/>
              <TextField fx:id="emailField" promptText="email"
                   prefWidth="150"/>
              <Button text="Add" onAction="#addPerson"/>
         </HBox>
    </GridPane>
    

Run the application and you will see that the text fields and button appear below the table, as shown in Figure 4-8. Enter data in the text fields and click Add to see the application in action.

Figure 4-8 Table with Text Fields and Button for Adding Data

Description of Figure 4-8 follows
Description of "Figure 4-8 Table with Text Fields and Button for Adding Data"

Where to Go from Here

This concludes the Address Book tutorial, but here are some things for you to try next:

  • Provide a filter to verify that data was entered in the correct format.

  • Customize the table by applying a cascading style sheet to distinguish between empty and non-empty rows. See "Styling UI Controls with CSS" in JavaFX UI Controls for more information.

  • Enable editing of data in the table. See Editing Data in the Table in Using JavaFX UI Controls for pointers.

  • See Deployment of FXML Applications for additional deployment options.

  • Look at Introduction to FXML, which provides more information on the elements that make up the FXML language. The document is included in the javafx.fxml package in the API documentation at http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html

  • For an example of an FXML application that uses data from a database, take a look at the Henley Sales Application sample by downloading the JavaFX Samples zip file at
    http://www.oracle.com/technetwork/java/javafx/downloads/

    The NetBeans projects and source code for this sample (called DataApp) are included in the samples zip file. See the readme for instructions on how to set up and run the application.

    This DataApp sample provides several examples of how to populate a table from a database. In particular, look at the following files:

    • DataAppClient\src\com\javafx\experiments\dataapp\client\historytab\history-tab.fxml

    • DataAppClient\src\com\javafx\experiments\dataapp\client\livetab\live-tab.fxml

    • DataAppClient\src\com\javafx\experiments\dataapp\client\productstab\products-tab.fxml

Previous
Next