Chapter 08: Graphics
January 9, 2026 | Previous part |
Java has a rich history of libraries for creating Graphical User Interfaces (GUIs) and working with graphics. From the original Abstract Window Toolkit (AWT) to the robust Swing library, and finally to the modern JavaFX platform, Java provides multiple ways to interact with users beyond the command line.
In this chapter, we will explore the evolution of these tools. We will start with the basics of image manipulation with AWT, move to building windows with Swing, and conclude with the modern JavaFX platform and web architectures.
A Brief History of Java GUIs
To understand why there are so many ways to build a window in Java, it helps to know the history.
1. AWT (Abstract Window Toolkit) - 1995
When Java 1.0 was released, it included AWT. AWT used the operating system’s native components (“peers”) to draw buttons and windows.
- Pros: Looked exactly like the native OS.
- Cons: “Write Once, Run Anywhere” was hard because different OSs behaved differently. It was “heavyweight” and limited to the lowest common denominator of features.
2. Swing (JFC) - 1998
Swing was built on top of AWT but rendered components itself using Java code. These were called “lightweight” components.
- Pros: Consistent look and feel across all platforms. Much richer set of components (Tables, Trees, Tabbed Panes).
- Cons: Could be slower than native apps. The default look (“Metal”) was often criticized.
- Status: Still widely used in enterprise legacy apps and simple tools (like the ones we build in class).
3. JavaFX - 2008
JavaFX was designed to replace Swing. It uses hardware acceleration (GPU) for modern graphics, supports CSS for styling, and FXML for layout.
- Pros: Modern look, 3D support, animation support, separates design from logic.
- Cons: Not included in the JDK by default anymore (requires separate download/setup).
- Status: The recommended choice for new desktop applications.
4. The Web Era (Spring Boot) - Present
Today, many developers use Java for the backend (server) and build the frontend using Web technologies (HTML/JavaScript/React). However, for desktop tools, scientific applications, and internal enterprise software, JavaFX and Swing remain powerful choices.
What You Will Learn
- AWT Graphics: How to load, modify, and save images using
BufferedImageandGraphics2D. - Swing Components: How to build windows using
JFrame,JPanel,JButton, andJTextField. - Advanced Swing: Using Menus, File Choosers, and Timers for animation.
- Dependency Management: How to use Maven or Gradle to manage external libraries like JavaFX.
- Modern GUIs with JavaFX: The basics of the JavaFX architecture (Stage, Scene, Nodes) and CSS styling.
- FXML: Separating your UI design from your Java code.
- Java and the Web: Understanding how Java backends (Spring Boot) interact with modern JavaScript frontends.
Working with Images and Graphics
Java provides powerful tools for manipulating images and graphics through the java.awt (Abstract Window Toolkit) package. Even without building a GUI, we can write programs that process image files, draw shapes, and create art.
Coming Attractions
Before we dive into the code, let’s meet the stars of the show. These are the classes you will use most often when working with 2D graphics in Java.
BufferedImage: The canvas. It represents an image stored in memory that you can modify.ImageIO: The loader/saver. It handles reading images from disk (JPG, PNG, GIF) and writing them back out.Graphics/Graphics2D: The artist. This object provides methods to draw lines, rectangles, ovals, and text onto aBufferedImage.Color: The palette. It represents colors using Red, Green, and Blue (RGB) components.Font: The typography. It defines the style, size, and family of the text you draw.
1. The Canvas: BufferedImage
The core class for handling images in memory is java.awt.image.BufferedImage. It represents an image with an accessible buffer of image data.
To read and write images, we use javax.imageio.ImageIO.
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class ImageLoader {
void main() throws Exception {
File file = new File("image.jpg");
BufferedImage image = ImageIO.read(file);
System.out.println("Width: " + image.getWidth());
System.out.println("Height: " + image.getHeight());
}
}
2. The Artist: Graphics2D
While you can manipulate pixels directly (as we’ll see later), it’s often easier to “draw” on an image. You can get a Graphics2D object from any BufferedImage.
BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
// Now we can draw!
g.setColor(Color.RED);
g.fillRect(0, 0, 500, 500); // Fill background with red
Drawing Shapes
The Graphics class has methods for all standard shapes:
drawLine(x1, y1, x2, y2)drawRect(x, y, width, height)/fillRect(...)drawOval(x, y, width, height)/fillOval(...)drawPolygon(xPoints, yPoints, nPoints)
Drawing Text
You can also draw text directly onto an image. This is how you might create a meme generator or add watermarks.
g.setFont(new Font("Arial", Font.BOLD, 48));
g.setColor(Color.WHITE);
g.drawString("Hello Java!", 50, 100);
3. The Palette: Color
The java.awt.Color class is used to set the current drawing color.
Built-in Colors
Java provides constants for standard colors: Color.RED, Color.BLUE, Color.GREEN, Color.BLACK, Color.WHITE, etc.
Custom Colors
You can create any color by specifying its Red, Green, and Blue (RGB) components (0-255).
Color myPurple = new Color(128, 0, 128);
g.setColor(myPurple);
Transparency (Alpha)
You can also add a fourth parameter for “Alpha” (transparency), where 0 is invisible and 255 is solid.
Color semiTransparentBlue = new Color(0, 0, 255, 128); // 50% see-through
4. Pixel Manipulation
Sometimes drawing shapes isn’t enough. You want to change the image pixel by pixel. We can access the color of any pixel using getRGB(x, y) and change it using setRGB(x, y, color).
Example: Inverting Colors
Here is a program that reads an image, inverts every pixel’s color (like a photo negative), and saves the result.
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Scanner;
import javax.imageio.ImageIO;
public class ImageInverter {
void main() throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter the path to an image file: ");
String filePath = scanner.nextLine();
File selectedFile = new File(filePath);
if (!selectedFile.exists()) {
System.out.println("File not found!");
return;
}
BufferedImage image = ImageIO.read(selectedFile);
// Process the image
invertColors(image);
// Save the new image
File outputFile = new File("inverted-image.jpg");
ImageIO.write(image, "jpg", outputFile);
System.out.println("Saved to " + outputFile.getAbsolutePath());
}
void invertColors(BufferedImage image) {
int w = image.getWidth();
int h = image.getHeight();
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
// Get current color
Color c = new Color(image.getRGB(x, y));
// Invert components
int newRed = 255 - c.getRed();
int newGreen = 255 - c.getGreen();
int newBlue = 255 - c.getBlue();
// Set new color
Color newColor = new Color(newRed, newGreen, newBlue);
image.setRGB(x, y, newColor.getRGB());
}
}
}
}
Project: The Meme Generator
Let’s combine Graphics2D, Font, and ImageIO to create a simple meme generator. This program will load an image and write text at the top and bottom.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class MemeGenerator {
void main() throws Exception {
// 1. Load the image
BufferedImage image = ImageIO.read(new File("cat.jpg"));
// 2. Get the Graphics object
Graphics2D g = image.createGraphics();
// 3. Enable Anti-aliasing for smooth text
g.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON
);
// 4. Setup Font
int fontSize = image.getWidth() / 10; // Scale font to image width
Font font = new Font("Impact", Font.BOLD, fontSize);
g.setFont(font);
// 5. Draw Text (Top)
String topText = "I CAN HAS";
drawCenteredString(g, topText, image.getWidth(), fontSize);
// 6. Draw Text (Bottom)
String bottomText = "JAVA?";
drawCenteredString(g, bottomText, image.getWidth(), image.getHeight() - 20);
// 7. Clean up and Save
g.dispose();
ImageIO.write(image, "jpg", new File("meme.jpg"));
}
void drawCenteredString(Graphics2D g, String text, int width, int y) {
// Calculate x to center the text
FontMetrics metrics = g.getFontMetrics();
int x = (width - metrics.stringWidth(text)) / 2;
// Draw Outline (Black)
g.setColor(Color.BLACK);
g.drawString(text, x+2, y+2);
// Draw Text (White)
g.setColor(Color.WHITE);
g.drawString(text, x, y);
}
}
This example demonstrates how powerful the standard library is. With just a few lines of code, we can perform complex image manipulation tasks.
Swing Components and Event Handling
While JOptionPane provides a quick way to pop up messages, most graphical applications require a persistent window with interactive elements like buttons, text fields, and labels. In Java’s Swing library, we build these interfaces using Components and Containers.
The Hierarchy of Swing
Swing components are organized in a hierarchy.
- Top-Level Containers: The main windows of your application. The most common is
JFrame. - Intermediate Containers: Panels that hold other components to organize the layout. The most common is
JPanel. - Atomic Components: The individual elements the user interacts with, such as
JButton,JLabel, andJTextField.
1. The Main Window: JFrame
Every Swing application starts with a JFrame. It represents a window with a title bar, border, and buttons to minimize, maximize, or close.
import javax.swing.JFrame;
public class SimpleWindow {
void main() {
// 1. Create the frame
JFrame frame = new JFrame("My First Window");
// 2. Set the size (width, height)
frame.setSize(400, 300);
// 3. Define what happens when the user clicks the 'X'
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 4. Make it visible
frame.setVisible(true);
}
}
setDefaultCloseOperation: By default, closing a window just hides it.EXIT_ON_CLOSEensures the program actually stops running.
2. Adding Components
You rarely add components directly to a JFrame. Instead, you add them to the frame’s Content Pane, or more commonly, you create a JPanel, add components to that panel, and then add the panel to the frame.
Common Components
JLabel: Displays read-only text or images.JButton: A clickable button that triggers an action.JTextField: A single-line input box for user text.
import javax.swing.*;
public class ComponentDemo {
void main() {
JFrame frame = new JFrame("Component Demo");
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a panel to hold our components
JPanel panel = new JPanel();
// Create components
JLabel label = new JLabel("Enter your name:");
JTextField nameField = new JTextField(15); // 15 columns wide
JButton button = new JButton("Say Hello");
// Add components to the panel
panel.add(label);
panel.add(nameField);
panel.add(button);
// Add the panel to the frame
frame.add(panel);
frame.setVisible(true);
}
}
3. Layout Managers
You might notice in the example above that the components just flow in a line. This is because JPanel uses a Layout Manager to decide where to put things.
Java provides several layout managers to control how components are arranged.
FlowLayout (Default for JPanel)
Arranges components in a line, wrapping to the next line if the window is too narrow. It centers them by default.
BorderLayout (Default for JFrame)
Divides the container into five regions: North, South, East, West, and Center.
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JButton("North"), BorderLayout.NORTH);
panel.add(new JButton("South"), BorderLayout.SOUTH);
panel.add(new JButton("Center"), BorderLayout.CENTER);
GridLayout
Arranges components in a grid of cells of equal size.
// 3 rows, 2 columns
JPanel panel = new JPanel(new GridLayout(3, 2));
panel.add(new JButton("1"));
panel.add(new JButton("2"));
// ... and so on
4. Event Handling: Making it Interactive
A GUI isn’t useful if it doesn’t do anything. We need to handle Events, such as a button click.
In Swing, we use an ActionListener. When a button is clicked, it fires an ActionEvent. We register a listener to “listen” for that event and run code when it happens.
The Old Way (Anonymous Inner Classes)
Before Java 8, this was verbose:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});
The Modern Way (Lambdas)
Since ActionListener is a functional interface (it has only one method), we can use a Lambda expression.
button.addActionListener(e -> {
System.out.println("Button clicked!");
});
Putting It All Together: The Greeter App
Let’s build a complete application. This app will have a label, a text field for the user’s name, and a button. When the button is clicked, it will update a message label.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class GreeterApp {
// We declare components as fields so we can access them inside methods
private JLabel messageLabel;
private JTextField nameField;
void main() {
// 1. Setup the Frame
JFrame frame = new JFrame("Greeter App");
frame.setSize(400, 150);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 2. Setup the Layout
// We'll use a BorderLayout for the main structure
frame.setLayout(new BorderLayout());
// 3. Create Top Panel (Input)
JPanel topPanel = new JPanel(); // Uses FlowLayout by default
topPanel.add(new JLabel("Name: "));
nameField = new JTextField(15);
topPanel.add(nameField);
JButton greetButton = new JButton("Greet Me!");
// Add the event listener
greetButton.addActionListener(e -> updateMessage());
topPanel.add(greetButton);
// 4. Create Center Panel (Output)
JPanel centerPanel = new JPanel();
messageLabel = new JLabel("Enter a name above.");
messageLabel.setFont(new Font("Arial", Font.BOLD, 18));
messageLabel.setForeground(Color.BLUE);
centerPanel.add(messageLabel);
// 5. Add panels to the frame
frame.add(topPanel, BorderLayout.NORTH);
frame.add(centerPanel, BorderLayout.CENTER);
// 6. Show it
frame.setVisible(true);
}
// Helper method called when button is clicked
private void updateMessage() {
String name = nameField.getText();
if (name.isBlank()) {
messageLabel.setText("Please enter a name!");
messageLabel.setForeground(Color.RED);
} else {
messageLabel.setText("Hello, " + name + "!");
messageLabel.setForeground(Color.BLUE);
}
}
}
Key Takeaways
- Separation of Concerns: We separated the layout code (in
main) from the logic code (updateMessage). - Accessing State: We used
nameField.getText()to read input andmessageLabel.setText()to change the UI. - Styling: We used
setFontandsetForegroundto make the text look better.
Simple Dialog Boxes (Recap)
As mentioned in the introduction, sometimes you don’t need a full window. For simple alerts, JOptionPane is still your best friend.
JOptionPane.showMessageDialog(parent, message): Show info.JOptionPane.showInputDialog(message): Get a string.JOptionPane.showConfirmDialog(parent, message): Get a Yes/No/Cancel response.
Swing is a massive library, but these building blocks—Frames, Panels, Components, Layouts, and Listeners—are the foundation of everything you will build with it.
Advanced Swing: Menus, Files, and Timers
Once you have mastered the basics of Frames, Panels, and Buttons, you can start adding more professional features to your Swing applications. In this section, we will look at adding Menu Bars, opening files, and creating simple animations.
1. The Menu Bar
Most desktop applications have a menu bar at the top of the window (File, Edit, View, etc.). In Swing, this is built using three components:
JMenuBar: The bar itself that sits at the top of the frame.JMenu: The dropdown categories (e.g., “File”).JMenuItem: The individual clickable options (e.g., “Open”, “Save”, “Exit”).
import javax.swing.*;
import java.awt.event.ActionEvent;
public class MenuDemo {
void main() {
JFrame frame = new JFrame("Menu Demo");
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 1. Create the Menu Bar
JMenuBar menuBar = new JMenuBar();
// 2. Create a Menu
JMenu fileMenu = new JMenu("File");
JMenu helpMenu = new JMenu("Help");
// 3. Create Menu Items
JMenuItem openItem = new JMenuItem("Open");
JMenuItem exitItem = new JMenuItem("Exit");
// 4. Add Actions
exitItem.addActionListener(e -> System.exit(0));
openItem.addActionListener(e -> {
JOptionPane.showMessageDialog(frame, "Open clicked!");
});
// 5. Assemble them
fileMenu.add(openItem);
fileMenu.addSeparator(); // Adds a line between items
fileMenu.add(exitItem);
menuBar.add(fileMenu);
menuBar.add(helpMenu);
// 6. Set the Menu Bar on the Frame
frame.setJMenuBar(menuBar);
frame.setVisible(true);
}
}
2. Opening Files with JFileChooser
We often need to let users select a file from their computer. The JFileChooser component provides a standard dialog for navigating the file system.
JFileChooser fileChooser = new JFileChooser();
// Optional: Set the starting directory to the user's home folder
fileChooser.setCurrentDirectory(new java.io.File(System.getProperty("user.home")));
// Show the "Open" dialog
int result = fileChooser.showOpenDialog(frame);
if (result == JFileChooser.APPROVE_OPTION) {
// User selected a file
java.io.File selectedFile = fileChooser.getSelectedFile();
System.out.println("Selected file: " + selectedFile.getAbsolutePath());
} else {
System.out.println("User cancelled the operation");
}
You can also use showSaveDialog(parent) if you want to save a file.
3. Simple Animation with javax.swing.Timer
Swing is single-threaded. If you write a while(true) loop in your main method to move an object, your GUI will freeze because the “Event Dispatch Thread” (EDT) is blocked.
To perform repetitive tasks (like animation) without freezing the UI, use javax.swing.Timer.
Note: Be careful not to import java.util.Timer, which is a different class.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class BouncingBall {
int x = 0;
int y = 100;
int xSpeed = 5;
void main() {
JFrame frame = new JFrame("Animation");
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Custom panel for drawing
JPanel panel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // Clear the screen
g.setColor(Color.RED);
g.fillOval(x, y, 30, 30); // Draw the ball
}
};
frame.add(panel);
frame.setVisible(true);
// Create a Timer that fires every 30 milliseconds (~33 FPS)
Timer timer = new Timer(30, e -> {
// Update logic
x += xSpeed;
// Bounce off walls
if (x > panel.getWidth() - 30 || x < 0) {
xSpeed = -xSpeed;
}
// Trigger a repaint
panel.repaint();
});
timer.start();
}
}
How it Works
Timer(delay, listener): Creates a timer that calls the listener’sactionPerformedmethod everydelaymilliseconds.panel.repaint(): Tells Swing that the component needs to be redrawn. Swing will callpaintComponentas soon as possible.paintComponent(Graphics g): This is where we do our custom drawing. Always callsuper.paintComponent(g)first to ensure the background is cleared.
By combining Timer, paintComponent, and event listeners, you can build simple games like Pong or Snake entirely in Swing!
Setting Up JavaFX with Build Tools
Unlike Swing, JavaFX is not included in the standard JDK (since Java 11). This means you cannot just import javafx.* and run your code; you must download the JavaFX library and tell your Java environment where to find it.
While you can manually download the SDK and configure your classpath (as shown in the “Manual Setup” section below), the professional way to manage dependencies in Java is using a Build Tool.
What is a Build Tool?
A build tool automates the process of compiling your code, managing libraries (dependencies), and packaging your application. The two most popular build tools in the Java ecosystem are Maven and Gradle.
Instead of downloading .jar files and manually adding them to your project, you simply list the libraries you need in a configuration file. The build tool downloads them from a central repository (like Maven Central) and ensures they are available to your program.
Option 1: Using Maven
Maven uses an XML file called pom.xml (Project Object Model) to describe your project.
1. Create a Maven Project
In VS Code, you can create a new Maven project using the “Create Java Project” command and selecting “Maven”.
2. The pom.xml File
To use JavaFX, you add the dependencies to your pom.xml.
<dependencies>
<!-- JavaFX Controls -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>21</version>
</dependency>
<!-- JavaFX FXML (if using FXML layouts) -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>21</version>
</dependency>
</dependencies>
3. The JavaFX Plugin
To run your application easily, you should also add the JavaFX Maven Plugin.
<build>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>com.example.App</mainClass>
</configuration>
</plugin>
</plugins>
</build>
Now, you can run your app from the terminal with:
mvn javafx:run
Option 2: Using Gradle
Gradle is a newer build tool that uses a Groovy or Kotlin-based DSL (Domain Specific Language) instead of XML. It is often considered more flexible and faster than Maven.
1. The build.gradle File
In your build.gradle file, you apply the JavaFX plugin and specify the version.
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.13'
}
javafx {
version = "21"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
application {
mainClass = 'com.example.App'
}
repositories {
mavenCentral()
}
Now, you can run your app with:
./gradlew run
Option 3: Manual Setup (The Hard Way)
If you are writing a simple script and don’t want to set up a full Maven/Gradle project, you can still do it manually.
1. Download the JavaFX SDK
- Go to the Gluon JavaFX download page.
- Download the SDK for your operating system (Windows, macOS, or Linux).
- Extract the downloaded zip file to a permanent location on your computer (e.g.,
C:\Program Files\Java\javafx-sdk-21or/Users/Shared/Java/javafx-sdk-21).
2. Configuring VS Code
If you are using Visual Studio Code with the Java Extension Pack:
- Open the Java Projects view in the Side Bar.
- Find your project and look for Referenced Libraries.
- Click the + icon.
- Navigate to the
libfolder inside your downloaded JavaFX SDK. - Select all the
.jarfiles (e.g.,javafx.base.jar,javafx.controls.jar, etc.) and click Select Jar Libraries.
3. Running from Command Line
When running manually, you must specify the Module Path.
java --module-path /path/to/javafx-sdk-21/lib --add-modules javafx.controls,javafx.fxml MyJavaFXApp.java
--module-path: Points to thelibfolder of the SDK.--add-modules: Lists the JavaFX modules your app needs.
Summary
- Maven/Gradle: Best for real projects. Handles downloads and classpath automatically.
- Manual: Good for quick experiments, but requires manual configuration every time.
For this course, we recommend trying to set up a Maven project, as it is the industry standard for Java dependency management.
Introduction to JavaFX
JavaFX is the modern standard for building rich client applications in Java. Unlike Swing, which relies on the CPU for rendering, JavaFX uses hardware acceleration (GPU) for smooth graphics. It also supports modern features like CSS styling, FXML for layout definitions, and 3D graphics.
The Structure of a JavaFX App
A JavaFX application is analogous to a theater play:
- Stage: The main window (the theater stage).
- Scene: The content currently displayed (a specific scene in the play).
- Nodes: The actors and props (buttons, text, shapes) arranged in a hierarchy called the Scene Graph.
A Simple JavaFX Program
To create a JavaFX application, we typically extend the javafx.application.Application class and override the start method.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloFX extends Application {
@Override
public void start(Stage stage) {
// 1. Create a Node (Control)
Label label = new Label("Hello, JavaFX!");
// 2. Create a Layout (Root Node) and add the label
StackPane root = new StackPane();
root.getChildren().add(label);
// 3. Create a Scene with the root node and size
Scene scene = new Scene(root, 300, 200);
// 4. Set the Scene on the Stage and show it
stage.setTitle("My First JavaFX App");
stage.setScene(scene);
stage.show();
}
// Standard main method to launch the app
public static void main(String[] args) {
launch();
}
}
Key Components
Application: The entry point. Thelaunch()method sets up the JavaFX runtime.start(Stage stage): This is where we build our UI. Thestageis provided by the system.Scene: Holds the content. We pass the root node (layout) and dimensions (width, height).StackPane: A simple layout pane that centers its children.
Common Controls
JavaFX comes with a huge library of UI controls. Here are a few you will use often:
Button: A clickable button.Label: Read-only text.TextField: Single-line text input.TextArea: Multi-line text input.CheckBox: A box that can be checked or unchecked.RadioButton: Used in groups where only one can be selected.ComboBox: A dropdown list of options.ListView: A scrollable list of items.
Button btn = new Button("Click Me");
TextField nameField = new TextField();
CheckBox agreeBox = new CheckBox("I agree to the terms");
Layout Panes
Just like Swing has Layout Managers, JavaFX has Layout Panes. These are containers that automatically arrange their children.
1. VBox and HBox
VBox arranges children vertically in a single column. HBox arranges them horizontally in a single row.
VBox vbox = new VBox(10); // 10 pixels of spacing between items
vbox.getChildren().addAll(new Label("Name:"), new TextField(), new Button("Submit"));
2. GridPane
Arranges children in a flexible grid of rows and columns.
GridPane grid = new GridPane();
grid.add(new Label("Username:"), 0, 0); // Column 0, Row 0
grid.add(new TextField(), 1, 0); // Column 1, Row 0
grid.add(new Label("Password:"), 0, 1); // Column 0, Row 1
grid.add(new PasswordField(), 1, 1); // Column 1, Row 1
3. BorderPane
Similar to Swing’s BorderLayout, it has Top, Bottom, Left, Right, and Center regions.
Styling with CSS
One of the coolest features of JavaFX is that you can style your application using CSS (Cascading Style Sheets), just like a website.
You can apply styles directly in code:
btn.setStyle("-fx-background-color: #ff0000; -fx-text-fill: white;");
Or, better yet, load a .css file:
/* style.css */
.button {
-fx-background-color: slateblue;
-fx-text-fill: white;
-fx-font-size: 14px;
}
scene.getStylesheets().add("style.css");
Example: A Login Form
Let’s put it all together to build a simple login form.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LoginForm extends Application {
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX Welcome");
// Create a GridPane layout
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
// Add Title
Text scenetitle = new Text("Welcome");
scenetitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20));
grid.add(scenetitle, 0, 0, 2, 1);
// Add Label and TextField
Label userName = new Label("User Name:");
grid.add(userName, 0, 1);
TextField userTextField = new TextField();
grid.add(userTextField, 1, 1);
// Add Label and PasswordField
Label pw = new Label("Password:");
grid.add(pw, 0, 2);
PasswordField pwBox = new PasswordField();
grid.add(pwBox, 1, 2);
// Add Button
Button btn = new Button("Sign in");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(btn);
grid.add(hbBtn, 1, 4);
// Add Text for messages
final Text actiontarget = new Text();
grid.add(actiontarget, 1, 6);
// Handle Button Click
btn.setOnAction(e -> {
actiontarget.setFill(javafx.scene.paint.Color.FIREBRICK);
actiontarget.setText("Sign in button pressed");
});
Scene scene = new Scene(grid, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This example shows how to combine layouts (GridPane, HBox), controls (Label, TextField, Button), and event handling (setOnAction) to create a functional UI.
JavaFX with FXML
In our previous JavaFX examples, we built the UI using Java code (e.g., new Button(), grid.add(...)). While this works for small apps, it mixes the Presentation (how it looks) with the Logic (how it works).
In modern development, we prefer to separate these concerns. JavaFX uses an XML-based language called FXML to define the user interface.
What is FXML?
FXML is a declarative markup language. It looks a lot like HTML, but for JavaFX components.
Java Code:
VBox root = new VBox();
Button btn = new Button("Click Me");
root.getChildren().add(btn);
FXML:
<VBox xmlns:fx="http://javafx.com/fxml">
<Button text="Click Me"/>
</VBox>
The Controller
When you define your UI in FXML, you need a Java class to handle the events (like button clicks). This is called the Controller.
We link the FXML file to the Controller using the fx:controller attribute.
1. The FXML File (hello-view.fxml)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>
fx:controller: Points to the Java class that manages this view.fx:id: Gives the element a name so we can access it in Java (like a variable name).onAction: Specifies the method name to call when clicked.
2. The Controller Class (HelloController.java)
package com.example;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class HelloController {
@FXML
private Label welcomeText;
@FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
}
@FXML: This annotation tells JavaFX to “inject” the component defined in the FXML file into this variable. The variable name must match thefx:id.
3. The Main Application (HelloApplication.java)
Finally, we need to load the FXML file in our main application.
package com.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Scene Builder
Writing XML by hand can be tedious. Fortunately, Gluon provides a free drag-and-drop tool called Scene Builder.
- Download and install Scene Builder.
- Open your
.fxmlfile in Scene Builder. - Drag components from the library onto the canvas.
- Set properties (text, color, size) in the Inspector panel.
- Assign
fx:idandonActionmethods in the Code panel. - Save the file.
Scene Builder generates the FXML code for you, allowing you to design complex interfaces visually.
Why Use FXML?
- Separation of Concerns: Designers can work on the FXML/CSS while developers work on the Java logic.
- Maintainability: It’s easier to see the structure of the UI in a hierarchical XML file than in nested Java method calls.
- Tooling: You can use visual tools like Scene Builder.
Most professional JavaFX applications use FXML for their layouts.
Java and the Web: The Future of GUIs
Throughout this chapter, we have explored how to build desktop applications using AWT, Swing, and JavaFX. These technologies are powerful for creating standalone tools, scientific applications, and internal enterprise software.
However, if you look at the software you use every day (Gmail, Netflix, Instagram), most of it runs in a web browser or a mobile app. In the modern software landscape, Java is rarely used for the “Frontend” (the part you see). Instead, it is a dominant force in the “Backend” (the server that processes data).
The Modern Architecture: Full Stack Development
In a modern web application, the responsibilities are split into two distinct layers. This is often called “Full Stack” development when you work on both.
- The Frontend (Client): Runs in the user’s browser. It handles the UI, animations, and user interaction. It is almost always written in JavaScript or TypeScript, using frameworks like React, Vue, or Angular.
- The Backend (Server): Runs in a data center. It handles security, database access, and business logic. This is where Java shines.
How They Talk: REST APIs and JSON
Since the Frontend (JavaScript) and Backend (Java) speak different languages, they communicate using a standard format called JSON (JavaScript Object Notation) over HTTP requests. This architectural style is called REST (Representational State Transfer).
1. The Frontend asks for data: The JavaScript code running in Chrome or Safari sends a request to the server.
// JavaScript (React/Vue)
fetch('https://api.myapp.com/users/1')
.then(response => response.json())
.then(data => console.log(data));
2. The Backend replies with data: The Java code running on the server receives the request, looks up the user in a database, and sends back a JSON string.
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
Going Deeper: Spring Boot
While you can write a web server in Java from scratch using raw Sockets (as we might see in a networking chapter), nobody does this in production. Instead, the industry standard is Spring Boot.
Spring Boot is a framework that makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.
Why Spring Boot?
When students learn “Advanced Java”, they often expect to learn complex Object-Oriented patterns. However, in the real world, “Advanced Java” usually means Spring.
- Dependency Injection (DI): Spring manages your objects for you. You don’t say
new DatabaseConnection(); you ask Spring to give you one. - Web Server Included: Spring Boot includes a web server (Tomcat) inside your app. You just run your
mainmethod, and the server starts. - Auto-Configuration: It guesses what you need based on the libraries you added. Added a database driver? It automatically sets up the connection pool.
A Real World Example
Let’s look at how a Spring Boot application is structured. It typically follows a Controller-Service-Repository pattern.
1. The Data Model (Record)
First, we define what our data looks like. In modern Java, we use record for this.
public record User(Long id, String name, String email) {}
2. The Controller (The Interface)
The Controller is the “Receptionist”. It accepts the HTTP request from the JavaScript frontend and decides what to do.
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:3000") // Allow React to talk to us
public class UserController {
private final UserService userService;
// Constructor Injection: Spring provides the UserService automatically
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.findById(id);
}
}
@RestController: Tells Spring this class handles web requests.@GetMapping: Maps a URL (like/api/users) to a Java method.@CrossOrigin: Allows the frontend (running on port 3000) to talk to the backend (running on port 8080).
3. The Service (The Logic)
The Service layer contains the business logic.
@Service
public class UserService {
// In a real app, we would inject a Repository here to talk to a database
public List<User> findAll() {
return List.of(
new User(1L, "Alice", "alice@example.com"),
new User(2L, "Bob", "bob@example.com")
);
}
public User findById(Long id) {
// Simulate finding a user
return new User(id, "Found User", "user" + id + "@example.com");
}
}
Does this require Spring?
Strictly speaking, no. You can build Java web APIs using:
- Jakarta EE (formerly Java EE): The official standard, but often considered more “heavyweight”.
- Micronaut / Quarkus: Newer, faster frameworks designed for the cloud.
- Javalin: A lightweight library for simple APIs.
However, Spring Boot is the overwhelming market leader. If you get a job as a Java Developer today, there is a 90% chance you will be writing Spring Boot code.
Summary
- Desktop (Swing/JavaFX): Great for high-performance, offline, or complex tools (like IDEs, scientific visualizers).
- Web (React + Spring Boot): The industry standard for consumer-facing applications.
- Frontend: JavaScript/React handles the visuals.
- Backend: Java/Spring Boot handles the logic and data.
- Communication: They talk via JSON over HTTP.
As you continue your programming journey, mastering Core Java is step one. Step two is often learning Spring Boot to take your Java skills to the web.