Java Programming GUI Framework Study Guide

JavaFX - Modern GUI Development

Master JavaFX: Learn to create modern, rich desktop applications with Scene Builder, FXML, CSS styling, animations, charts, and 3D graphics using Java's premier GUI framework.

What is JavaFX?

JavaFX is a set of graphics and media APIs that enables developers to create feature-rich desktop applications. Key features include:

FeatureDescription
Hardware-accelerated graphicsUses GPU for smooth rendering
CSS stylingStyle UI with familiar CSS syntax
FXMLDeclarative XML-based UI markup
Rich controls libraryTables, trees, web views, charts
Media supportAudio and video playback
2D/3D graphicsBuilt-in shapes and 3D transformations
Animation APISmooth timeline-based animations
Diagram of JavaFX architecture: Stage, Scene, layout panes, controls, and CSS styling
JavaFX: Stage = window; Scene = content; layout panes arrange nodes; style with CSS.

Setup and Installation

JDK Compatibility

  • Java 8: JavaFX is bundled with the JDK
  • Java 11+: JavaFX must be added separately (removed from JDK)

Maven Configuration (Java 11+)

Add these dependencies to your pom.xml:

pom.xml
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>21</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-fxml</artifactId>
    <version>21</version>
</dependency>

Gradle Configuration

build.gradle
dependencies {
    implementation 'org.openjfx:javafx-controls:21'
    implementation 'org.openjfx:javafx-fxml:21'
}

IDE Setup (IntelliJ IDEA)

When running, add these VM options:

VM Options
--module-path /path/to/javafx-sdk/lib --add-modules javafx.controls,javafx.fxml

Your First JavaFX Application (Hello World)

HelloWorld.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {

    @Override
    public void start(Stage primaryStage) {
        // Create a button
        Button btn = new Button();
        btn.setText("Say 'Hello World'");

        // Add event handler
        btn.setOnAction(event -> {
            System.out.println("Hello World!");
        });

        // Create layout and add button
        StackPane root = new StackPane();
        root.getChildren().add(btn);

        // Create scene (300x250 pixels)
        Scene scene = new Scene(root, 300, 250);

        // Set up stage and show
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

To run:

Terminal
javac HelloWorld.java
java HelloWorld

What's happening:

  • Your class extends Application
  • The start() method is the main entry point
  • launch(args) initializes the JavaFX runtime

Core Concepts: Stage, Scene, and Nodes

JavaFX uses a hierarchical structure:

Hierarchy
Stage (Window)
  └── Scene (Container)
       └── Scene Graph (Tree of Nodes)
            ├── Button
            ├── Label
            └── Layout Container
                 └── More Nodes...

Stage (Window)

Stage
Stage stage = new Stage();
stage.setTitle("My Window");
stage.setWidth(400);
stage.setHeight(300);
stage.setResizable(true);
stage.show();

Scene

Scene
// Scene with node and size
Button button = new Button("Click Me");
Scene scene = new Scene(button, 400, 300);

// Scene with layout
VBox vbox = new VBox(button);
Scene scene2 = new Scene(vbox, 400, 300);

Node Properties (Common to all UI elements)

Node properties
Button btn = new Button("Submit");
btn.setVisible(true);
btn.setDisable(false);
btn.setOpacity(0.8);
btn.setStyle("-fx-background-color: blue;");

Layouts and UI Controls

Common Layouts

LayoutDescription
VBoxVertical arrangement
HBoxHorizontal arrangement
GridPaneGrid layout (rows and columns)
BorderPaneTop, bottom, left, right, center regions
StackPaneStacked nodes (one on top of another)

Layout Examples

VBox (Vertical):

VBox
VBox vbox = new VBox(10); // 10px spacing
vbox.getChildren().addAll(
    new Label("Username:"),
    new TextField(),
    new Label("Password:"),
    new PasswordField(),
    new Button("Login")
);

GridPane (Form layout):

GridPane
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);

grid.add(new Label("Name:"), 0, 0);
grid.add(new TextField(), 1, 0);
grid.add(new Label("Email:"), 0, 1);
grid.add(new TextField(), 1, 1);

BorderPane (Main layout):

BorderPane
BorderPane border = new BorderPane();
border.setTop(new Label("Header"));
border.setCenter(new TextArea("Main content"));
border.setBottom(new HBox(new Button("OK"), new Button("Cancel")));

Common UI Controls

UI Controls
// Basic controls
Label label = new Label("Enter data:");
TextField textField = new TextField();
PasswordField pwdField = new PasswordField();
Button button = new Button("Submit");
CheckBox checkbox = new CheckBox("Accept terms");
RadioButton radio = new RadioButton("Option 1");

// Selection controls
ComboBox<String> combo = new ComboBox<>();
combo.getItems().addAll("Option A", "Option B", "Option C");

// ListView
ListView<String> listView = new ListView<>();
listView.getItems().addAll("Item 1", "Item 2", "Item 3");

// TableView
TableView<Person> tableView = new TableView<>();

Event Handling

JavaFX uses a functional event handling model.

Button Click Event

Button events
Button btn = new Button("Click Me");

// Lambda expression (modern approach)
btn.setOnAction(event -> {
    System.out.println("Button clicked!");
});

// Method reference
btn.setOnAction(this::handleButtonClick);

Mouse Events

Mouse events
btn.setOnMouseEntered(event -> {
    btn.setStyle("-fx-background-color: lightblue;");
});

btn.setOnMouseExited(event -> {
    btn.setStyle("-fx-background-color: lightgray;");
});

btn.setOnMouseClicked(event -> {
    if (event.getClickCount() == 2) {
        System.out.println("Double-click detected!");
    }
});

Keyboard Events

Keyboard events
Scene scene = new Scene(root, 400, 300);
scene.setOnKeyPressed(event -> {
    switch (event.getCode()) {
        case ENTER:
            System.out.println("Enter pressed");
            break;
        case ESCAPE:
            System.out.println("Escape pressed");
            break;
        default:
            break;
    }
});

TextField Events

TextField listener
TextField tf = new TextField();
tf.textProperty().addListener((observable, oldValue, newValue) -> {
    System.out.println("Text changed from '" + oldValue + "' to '" + newValue + "'");
});

CSS Styling

JavaFX uses CSS-like syntax prefixed with -fx-.

Inline Styling

Inline CSS
Button btn = new Button("Styled Button");
btn.setStyle(
    "-fx-background-color: #4CAF50; " +
    "-fx-text-fill: white; " +
    "-fx-font-size: 14px; " +
    "-fx-padding: 10px 20px;"
);

External CSS File (style.css)

style.css
/* style.css */
.root {
    -fx-font-family: "Segoe UI";
    -fx-base: #f0f0f0;
}

.button {
    -fx-background-color: linear-gradient(to bottom, #4CAF50, #45a049);
    -fx-text-fill: white;
    -fx-font-size: 14px;
    -fx-padding: 10px 20px;
    -fx-background-radius: 5px;
}

.button:hover {
    -fx-background-color: linear-gradient(to bottom, #45a049, #3e8e41);
    -fx-cursor: hand;
}

.button:pressed {
    -fx-background-color: #3e8e41;
}

.label {
    -fx-font-size: 12px;
    -fx-text-fill: #333333;
}

.text-field {
    -fx-padding: 8px;
    -fx-border-color: #cccccc;
    -fx-border-radius: 4px;
}

.text-field:focused {
    -fx-border-color: #4CAF50;
}

Loading CSS

Load CSS
Scene scene = new Scene(root, 600, 400);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
// or
scene.getStylesheets().add("file:///path/to/style.css");

FXML (Declarative UI)

FXML separates UI layout from application logic using XML.

FXML File (login.fxml)

login.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.Button?>

<VBox xmlns="http://javafx.com/javafx/21"
      xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="LoginController"
      spacing="10" alignment="CENTER"
      style="-fx-padding: 20;">

    <Label text="Login Form" style="-fx-font-size: 20px;"/>
    <TextField fx:id="usernameField" promptText="Username"/>
    <PasswordField fx:id="passwordField" promptText="Password"/>
    <Button text="Login" onAction="#handleLogin"/>
    <Label fx:id="messageLabel" style="-fx-text-fill: red;"/>

</VBox>

Controller Class (LoginController.java)

LoginController.java
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;

public class LoginController {

    @FXML
    private TextField usernameField;

    @FXML
    private PasswordField passwordField;

    @FXML
    private Label messageLabel;

    @FXML
    private void handleLogin() {
        String username = usernameField.getText();
        String password = passwordField.getText();

        if ("admin".equals(username) && "secret".equals(password)) {
            messageLabel.setStyle("-fx-text-fill: green;");
            messageLabel.setText("Login successful!");
        } else {
            messageLabel.setStyle("-fx-text-fill: red;");
            messageLabel.setText("Invalid credentials!");
        }
    }
}

Loading FXML in Main Application

MainApp.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MainApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("login.fxml"));
        primaryStage.setTitle("Login Application");
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }

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

Animation and Effects

Transitions (Built-in Animations)

Transitions
import javafx.animation.*;
import javafx.util.Duration;

Button btn = new Button("Animate Me");

// Fade transition
FadeTransition fade = new FadeTransition(Duration.seconds(2), btn);
fade.setFromValue(1.0);
fade.setToValue(0.3);
fade.setCycleCount(Animation.INDEFINITE);
fade.setAutoReverse(true);
fade.play();

// Translate (move) transition
TranslateTransition translate = new TranslateTransition(Duration.seconds(1), btn);
translate.setByX(100);
translate.setByY(50);
translate.setCycleCount(2);
translate.setAutoReverse(true);
translate.play();

// Rotate transition
RotateTransition rotate = new RotateTransition(Duration.seconds(2), btn);
rotate.setByAngle(360);
rotate.setCycleCount(Animation.INDEFINITE);
rotate.play();

// Scale transition
ScaleTransition scale = new ScaleTransition(Duration.seconds(1), btn);
scale.setToX(1.5);
scale.setToY(1.5);
scale.setAutoReverse(true);
scale.setCycleCount(Animation.INDEFINITE);
scale.play();

Timeline (Custom Animations)

Timeline
Timeline timeline = new Timeline();
KeyFrame keyFrame = new KeyFrame(
    Duration.seconds(2),
    event -> System.out.println("2 seconds passed!")
);
timeline.getKeyFrames().add(keyFrame);
timeline.setCycleCount(3);
timeline.play();

Visual Effects

Effects
import javafx.scene.effect.*;

Button btn = new Button("Effect Button");

// Drop shadow
DropShadow dropShadow = new DropShadow();
dropShadow.setRadius(5.0);
dropShadow.setOffsetX(3.0);
dropShadow.setOffsetY(3.0);
btn.setEffect(dropShadow);

// Gaussian blur
GaussianBlur blur = new GaussianBlur();
blur.setRadius(5);
btn.setEffect(blur);

// Reflection
Reflection reflection = new Reflection();
reflection.setFraction(0.7);
btn.setEffect(reflection);

Working with Command Line Arguments

JavaFX applications can access command-line arguments through the Application.Parameters class.

Parameters example
import javafx.application.Application;
import javafx.stage.Stage;
import java.util.List;

public class ArgsApp extends Application {
    @Override
    public void start(Stage stage) {
        List<String> params = getParameters().getRaw();
        List<String> unnamed = getParameters().getUnnamed();
        System.out.println("Raw parameters: " + params);
        System.out.println("Unnamed arguments: " + unnamed);
    }

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

Run with named and unnamed parameters:

Terminal
java ArgsApp --mode=debug input.txt output.txt

Summary and Best Practices

Best PracticeReason
Use Platform.runLater() for UI updates from threadsPrevents threading errors
Separate UI from logic with FXMLEasier maintenance and testing
Use CSS for stylingConsistent look across application
Bind properties rather than manually updatingReduces boilerplate code
Handle stage close eventsClean up resources
Follow MVC/MVP patternsBetter code organization