Building JavaFX User Interface Using FXML

By Building JavaFX User Interface Using FXML

In the previous article, we learnt about writing your first JavaFX application, the internals of a simple JavaFX application, and the lifecycle of a JavaFX application.  If you want to understand the basics of all JavaFX applications, I encourage you to read the article.

In this article, we will build a JavaFX user interface using FXML. FXML is an XML based language provided by JavaFX. We will use Scene Builder to drag and drop components to come up with a beautiful looking UI.

When using FXML, we can follow MVC (Model View Controller) design pattern because it allows us to write user interface separate from the application' logic i.e., Java.  The "Model" consists of application-specific domain entities, the "View" consists of FXML (user interface), and the "Controller" is the Java code that defines the GUI's behavior for interacting with the user. MVC makes code easier to maintain.

In this article, I'll show you how to create a login form using JavaFX.

Creating a Login Form using JavaFX in JavaFX Scene Builder

Open your IDE and create a new JavaFX project called javafx-login. My favorite Integrated Development Environment is NetBeans IDE. I have a list of best IDEs in this article.

After creating the project, create a package named javafx-login and create three files.

  1. LoginFormApp.java
  2. LoginController.java
  3. LoginForm.fxml

Code for LoginForm.fxml

This is an FXML document that contains the user interface for our application. It will have textfield for username, passwordfield for the password, a button to validate details, and some labels to give more information to the user. 

We created an empty VBox and added a label and GridPane. Inside the GridPane, we created three rows and two columns. GridPane layout offers us a smooth way of laying out the rest ui-controls.

The following is the content of the document:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Label alignment="CENTER" maxWidth="1.7976931348623157E308" minWidth="-Infinity" text="JavaFX Login Form">
         <font>
            <Font size="48.0" />
         </font>
         <VBox.margin>
            <Insets bottom="10.0" top="10.0" />
         </VBox.margin>
      </Label>
      <GridPane prefHeight="185.0" prefWidth="580.0">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" maxWidth="283.0" minWidth="10.0" prefWidth="162.0" />
          <ColumnConstraints hgrow="SOMETIMES" maxWidth="418.0" minWidth="10.0" prefWidth="418.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints maxHeight="57.0" minHeight="10.0" prefHeight="57.0" vgrow="SOMETIMES" />
          <RowConstraints maxHeight="64.0" minHeight="10.0" prefHeight="64.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="64.0" minHeight="10.0" prefHeight="64.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <Label text="Username" GridPane.halignment="RIGHT">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin>
               <font>
                  <Font size="20.0" />
               </font>
            </Label>
            <Label layoutX="10.0" layoutY="17.0" text="Password" GridPane.halignment="RIGHT" GridPane.rowIndex="1">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin>
               <font>
                  <Font size="20.0" />
               </font>
            </Label>
            <TextField GridPane.columnIndex="1">
               <font>
                  <Font size="20.0" />
               </font>
            </TextField>
            <PasswordField GridPane.columnIndex="1" GridPane.rowIndex="1">
               <font>
                  <Font size="20.0" />
               </font>
            </PasswordField>
            <Button alignment="CENTER" contentDisplay="CENTER" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" GridPane.rowIndex="2">
               <font>
                  <Font size="20.0" />
               </font>
            </Button>
         </children>
         <VBox.margin>
            <Insets left="10.0" right="10.0" />
         </VBox.margin>
      </GridPane>
   </children>
</VBox>

In our FXML document, we defined the controller that will be used to handle any events such as mouse or keyboard using fx:controller property. I.e. fx:controller= "javafx.login.LoginController". 

Our textfiled and passwordfiled have ids that are defined using fx:id property. We will use these ids to get text from these fields. The ids, just like variable names, should be unique.

Our submit button has an onAction property. This calls a method with the same name defined in our login controller class. 

I used JavaFX Scene Builder to create this document. Download Gluon Scene Builder here.

Code for LoginController.java

This is the FXML controller which will handle events. It will lick with LoginForm.fxml to get the username, password, and the button. 

The FXMLLoader automatically injects values defined in our FXML document into corresponding references in the controller class. Hence we need to declare variables for username and password. And the names must match their ids in the FXML document. The @FXML annotation is required for private member fields. If this is not included, then injection will not work. 

The loginAction() method contains code that handles form submit. This method is also annotated with @FXML because it is not public.

The following is the controller code:

package javafx.login;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
public class LoginController {
    @FXML
    private TextField usernameField;
    @FXML
    private PasswordField passwordField;
    @FXML
    private void loginAction(ActionEvent event) {
        String username = usernameField.getText().trim();
        String password = passwordField.getText();
        if (username.isEmpty()) {
            showAlertMessage(Alert.AlertType.ERROR, "Username Required!",
                    "Please enter your username");
            return;
        }
        if (password.isEmpty()) {
            showAlertMessage(Alert.AlertType.ERROR, "Password Required!",
                    "Please enter your password");
            return;
        }
        showAlertMessage(Alert.AlertType.INFORMATION, "Details sent to database!",
                "Username and password have been sent to database for validation");
    }
    public static void showAlertMessage(Alert.AlertType alertType, String title, String message) {
        Alert alert = new Alert(alertType);
        alert.setTitle(title);
        alert.setHeaderText(null);
        alert.setContentText(message);
        alert.show();
    }
}

Inside the loginAction() method, we get text from username and password fields when the button is clicked. We use getText() method to read what the user typed in these fields.  We used isEmpty() method to check whether the user typed anything on the fields.

I created another method showAlertMessage() that shows error or success message to the user.

Note @FXML annotation can be omitted for public member fields, i.e. variables, and methods which are public.

Code for LoginFormApp.java

This is our Main Application class. Let us now write the Main Application class. As usual, we will need to extend javafx.application.Application and override its start() method.

In this class, we first load our FXML document using the FXMLLoader class. This references to the root node of the user interface. The root node in our FXML document is VBox.

After that, we will create a Scene using the FXML root node and set the scene on the primary stage.

Load FXML Document into Main Class

The following is the code for LoginFormApp.java:

package javafx.login;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class LoginFormApp extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("LoginForm.fxml"));
        primaryStage.setTitle("Login Form FXML Application");
        primaryStage.setScene(new Scene(root, 800, 500));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Application Output

When you successfully write the application, you will see the following window. These are the screenshots of our JavaFX login application.

Login Form:


Alert dialog with error message:


Alert dialog with success message:


You can get the complete code for the login form application from my GitHub repository. You may also make changes to the code and use it in your projects.

Conclusion

That is how to create a login form in JavaFX using FXML. FXML is a powerful tool. It enables us keep the code clean and easier to maintain because it separates the application design from application logic.

Thank you for reading my blog. Please share it with your friends and fellow developers.

Happy coding!

Was this article helpful?
Donate with PayPal: https://www.paypal.com/donate

Bessy
Eric Murithi Muchenah

Life is beautiful, time is precious. Make the most out of it.