Introduction
Java Swing is a graphical user interface toolkit included in the Java Development Kit. It allows developers to build cross-platform desktop applications using standard Java.
Swing provides many commonly used desktop components, including:
- Windows
- Buttons
- Text fields
- Drop-down lists
- Tables
- Dialog boxes
- Menu bars
- File selectors
Although many modern applications are developed as web applications, Swing is still used in mature enterprise desktop systems, internal business applications, engineering software, development tools, and applications that need to work offline.
In this post, I will first introduce the fundamental concepts and commonly used APIs in Java Swing. After that, I will use these components to build a simplified Residential Energy Rating Tool.
The energy calculation in this project is only for learning purposes. It is not an official NatHERS, FirstRate5, or regulatory energy assessment model.
Why Learn Java Swing?
Java Swing is particularly useful in the following situations:
- The application must run directly on a desktop operating system.
- The application needs to work without an internet connection.
- The project already has an existing Java Swing codebase.
- The system requires long-term stability and maintainability.
- The application needs access to local files or operating system resources.
- The development team wants to use Java for both business logic and the desktop interface.
Basic Structure of a Swing Application
A typical Swing application contains one main window and several child components.
1 | JFrame |
The most important Swing classes include:
| Class | Purpose |
|---|---|
| JFrame | Represents the main application window |
| JPanel | Groups and organizes components |
| JLabel | Displays text or images |
| JTextField | Accepts single-line text input |
| JTextArea | Accepts multi-line text input |
| JButton | Performs an action when clicked |
| JCheckBox | Represents a true or false option |
| JRadioButton | Allows selection from a group of options |
| JComboBox | Displays a drop-down list |
| JSpinner | Accepts values within a defined range |
| JTable | Displays tabular data |
| JScrollPane | Adds scrolling support |
| JOptionPane | Displays messages and confirmation dialogs |
| JFileChooser | Allows users to select files |
Creating The First Swing Window
The following example creates a basic desktop window.
1 | import javax.swing.*; |
The application performs the following steps:
- Creates a
JFrameobject, which represents the main window. - Adds a
JLabelcomponent to the window, which displays the text “Hello, Java Swing!”. - Sets the default close operation to exit the application when the window is closed.
- Sets the size and position of the window using
setBounds(). You can also usesetSize()andsetLocation()to set the size and position separately, or usesetLocationRelativeTo(null)to center the window on the screen. - Makes the window visible by calling
setVisible(true).
The Event Dispatch Thread (EDT)
Swing uses a special thread called the Event Dispatch Thread, commonly abbreviated as EDT.
The EDT is responsible for:
- Creating Swing components
- Updating the interface
- Handling button clicks
- Handling keyboard events
- Processing mouse events
- Repainting the application window
Swing components are generally not thread-safe. Therefore, Swing interfaces should be created and updated on the EDT.
The recommended application entry point is:
1 | public static void main(String[] args) { |
SwingUtilities.invokeLater()places the interface creation task on the Event Dispatch Thread.
Commonly Used Swing Components
JFrame
JFrame represents the main application window.
1 | JFrame frame = new JFrame("Residential Energy Rating Tool"); |
Commonly used methods include:
| Method | Description |
|---|---|
| setTitle() | Changes the window title |
| setSize() | Sets the window width and height |
| setMinimumSize() | Sets the minimum window size |
| setDefaultCloseOperation() | Defines the close behaviour |
| setLocationRelativeTo(null) | Centers the window |
| setResizable() | Controls whether resizing is allowed |
| setVisible(true) | Displays the window |
| pack() | Calculates the window size from its components |
In most applications, pack() is preferred over a hard-coded size because it calculates the required window size automatically.
1 | frame.pack(); |
JPanel
JPanel is a container used to group related components.
1 | JPanel panel = new JPanel(); |
A panel can also use a specific layout manager.
1 | JPanel panel = new JPanel(new BorderLayout()); |
Panels are commonly used to separate a page into logical sections, such as:
- Header area
- Input form
- Result panel
- Button area
- Table area
JLabel
JLabel displays text, icons, or images.
1 | JLabel titleLabel =new JLabel("Residential Energy Rating Tool"); |
A label can be aligned horizontally.
1 | titleLabel.setHorizontalAlignment(SwingConstants.CENTER); |
The font can also be customized.
1 | titleLabel.setFont(new Font("SansSerif", Font.BOLD, 24)); |
Labels are commonly used for:
- Form field names
- Page titles
- Result values
- Instructions
- Validation messages
JTextField
JTextField accepts a single line of text input.
1 | JTextField areaField = new JTextField(15); |
The number passed to the constructor represents the preferred number of visible columns.
Reading a value:
1 | String areaText = areaField.getText(); |
Removing spaces:
1 | String areaText = areaField.getText().trim(); |
Converting the value to a number:
1 | double floorArea =Double.parseDouble(areaField.getText().trim()); |
Clearing the field:
1 | areaField.setText(""); |
Setting an initial value:
1 | areaField.setText("150"); |
JTextArea
JTextArea accepts multi-line input.
1 | JTextArea notesArea =new JTextArea(6, 30); |
Enable line wrapping:
1 | notesArea.setLineWrap(true); |
A text area should normally be placed inside a JScrollPane.
1 | JScrollPane scrollPane =new JScrollPane(notesArea); |
JButton
JButton creates a clickable button.
1 | JButton calculateButton =new JButton("Calculate"); |
A button click is handled using an ActionListener.
1 | calculateButton.addActionListener(event -> { |
For better readability, the action can call a separate method.
1 | calculateButton.addActionListener(event -> calculateRating()); |
This is better than placing a large amount of business logic directly inside the listener.
JCheckBox
JCheckBox represents an option that can be selected or cleared.
1 | JCheckBox insulationCheckBox =new JCheckBox("Property has insulation"); |
Check whether the option is selected:
1 | boolean hasInsulation =insulationCheckBox.isSelected(); |
Select or clear the checkbox programmatically:
1 | insulationCheckBox.setSelected(true); |
Checkboxes are suitable for independent boolean options.
Examples include:
- Has insulation
- Has solar panels
- Include archived records
- Enable notifications
JRadioButton
JRadioButton is normally used when the user must choose one option from a group.
1 | JRadioButton houseButton =new JRadioButton("House"); |
The buttons must be placed inside a ButtonGroup.
1 | ButtonGroup propertyTypeGroup =new ButtonGroup(); |
Set a default selection:
1 | houseButton.setSelected(true); |
Read the selected option:
1 | if (houseButton.isSelected()) { |
JComboBox
JComboBox displays a drop-down list.
1 | JComboBox<String> heatingComboBox = |
Read the selected value:
1 | String heatingSystem =(String) heatingComboBox.getSelectedItem(); |
Select an item by index:
1 | heatingComboBox.setSelectedIndex(0); |
Listen for selection changes:
1 | heatingComboBox.addActionListener(event -> { |
JSpinner
JSpinner is useful when the user must enter a value within a defined range.
1 | JSpinner occupantSpinner = |
The values represent:
1 | Initial value: 2 |
Read the current value:
1 | int occupants =(Integer) occupantSpinner.getValue(); |
Change the value:
1 | occupantSpinner.setValue(4); |
A spinner is often safer than a text field for bounded numeric input.
JOptionPane
JOptionPane provides simple dialog boxes.
Information dialog
1 | JOptionPane.showMessageDialog(frame,"Calculation completed."); |
Error dialog
1 | JOptionPane.showMessageDialog(frame,"Please enter a valid floor area.","Invalid Input",JOptionPane.ERROR_MESSAGE); |
Warning dialog
1 | JOptionPane.showMessageDialog( |
Confirmation dialog
1 | int result = JOptionPane.showConfirmDialog(frame,"Do you want to clear the form?","Confirmation",JOptionPane.YES_NO_OPTION); |
Input dialog
1 | String propertyName =JOptionPane.showInputDialog(frame,"Enter the property name:"); |
JTable
JTable displays data in rows and columns.
The table itself displays the data, while a table model manages the underlying values.
1 | String[] columns = { |
Add a new row:
1 | tableModel.addRow(new Object[]{150,3,"Heat Pump","9,800 kWh/year","5 Stars"}); |
Remove a row:
1 | int selectedRow = table.getSelectedRow(); |
Read a cell value:
1 | Object value =tableModel.getValueAt(0, 2); |
A table should normally be placed inside a scroll pane.
1 | JScrollPane tableScrollPane =new JScrollPane(table); |
Disable direct cell editing:
1 | DefaultTableModel tableModel = new DefaultTableModel(columns, 0) { |
JScrollPane
JScrollPane adds horizontal or vertical scrolling to another component.
It is commonly used with:
- JTable
- JTextArea
- JList
- Large panels
- Images
Example:
1 | JTextArea textArea =new JTextArea(10, 40); |
JMenuBar
JMenuBar creates an application menu.
1 | JMenuBar menuBar = new JMenuBar(); |
Handle a menu action:
1 | exitItem.addActionListener(event -> System.exit(0)); |
JFileChooser
JFileChooser allows the user to select a file or directory.
Open a file
1 | JFileChooser fileChooser =new JFileChooser(); |
Save a file
1 | int result =fileChooser.showSaveDialog(frame); |
Swing Layout Managers
Layout managers control the position and size of Swing components.
Using layout managers is recommended because they can automatically adjust the interface when:
- The window is resized
- The font size changes
- The operating system changes
- Text labels have different lengths
- Components require different sizes
Common layout managers include:
| Layout Manager | Typical Use |
|---|---|
| BorderLayout | Main application structure |
| FlowLayout | Buttons and simple horizontal groups |
| GridLayout | Equal-sized rows and columns |
| GridBagLayout | Complex forms |
| BoxLayout | Horizontal or vertical groups |
| CardLayout | Multiple pages or views |
BorderLayout
BorderLayout divides a container into five regions.
1 | NORTH |
Example:
1 | JPanel mainPanel =new JPanel(new BorderLayout()); |
Only one component can be placed directly in each region. To place multiple components in one region, put them inside another panel.
FlowLayout
FlowLayout arranges components from left to right.
1 | JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
It is commonly used for:
- Button groups
- Toolbars
- Simple input rows
- GridLayout
GridLayout places components into equal-sized cells.
1 | JPanel panel =new JPanel(new GridLayout(2, 2, 10, 10)); |
The parameters represent:
1 | Rows: 2 |
GridBagLayout
GridBagLayout is more complex but provides better control for business forms.
1 | JPanel formPanel =new JPanel(new GridBagLayout()); |
Add the input field in the next column:
1 | constraints.gridx = 1; |
Important GridBagConstraints properties include:
| Property | Purpose |
|---|---|
| gridx | Column position |
| gridy | Row position |
| gridwidth | Number of columns occupied |
| gridheight | Number of rows occupied |
| weightx | Horizontal resizing weight |
| weighty | Vertical resizing weight |
| fill | Controls component expansion |
| anchor | Controls component alignment |
| insets | Adds external spacing |
BoxLayout
BoxLayout arranges components vertically or horizontally.
Vertical layout:
1 | JPanel panel = new JPanel(); |
Horizontal layout:
1 | panel.setLayout(new BoxLayout(panel,BoxLayout.X_AXIS)); |
Add spacing:
1 | panel.add(Box.createVerticalStrut(10)); |
CardLayout
CardLayout allows multiple panels to share the same display area.
1 | CardLayout cardLayout =new CardLayout(); |
Switch between panels:
1 | cardLayout.show(cardPanel,"dashboard"); |
It is useful for:
- Login and dashboard pages
- Multi-step forms
- Wizard interfaces
- Settings pages
- Applications with multiple screens
Event Handling
Swing applications use an event-driven programming model.
An event occurs when the user performs an action, such as:
- Clicking a button
- Selecting a menu item
- Typing in a text field
- Changing a drop-down selection
- Selecting a table row
- Closing a window
- Pressing a keyboard key
ActionListener
ActionListener handles actions from buttons, menu items, and some other components.
1 | button.addActionListener(event -> {System.out.println("Button clicked");}); |
The traditional anonymous class syntax is:
1 | button.addActionListener( |
The lambda version is shorter and easier to read.
ItemListener
ItemListener can detect checkbox and selection state changes.
1 | insulationCheckBox.addItemListener(event -> { |
MouseListener
MouseListener handles mouse-related actions.
1 | table.addMouseListener( |
KeyListener
KeyListener handles keyboard events.
1 | textField.addKeyListener(new KeyAdapter() { |
For text changes, a DocumentListener is usually more reliable than a KeyListener.
DocumentListener
DocumentListener detects changes in a text component.
1 | textField.getDocument().addDocumentListener(new DocumentListener() { |
Input Validation
A desktop application should never assume that user input is valid.
For example, when reading a floor area:
1 | String areaText =areaField.getText().trim(); |
Validate the numeric format:
1 | double floorArea; |
Validate the range:
1 | if (floorArea <= 0) { |
Display the validation error:
1 | JOptionPane.showMessageDialog( |
A reliable application should validate:
- Required fields
- Number formats
- Date formats
- Minimum and maximum values
- File existence
- Conflicting selections
- Business rules
- Duplicate records
Long-Running Tasks and SwingWorker
The Event Dispatch Thread should only perform short UI operations.
The following code may freeze the interface:
1 | calculateButton.addActionListener(event -> { |
If these operations take several seconds, the application window may become unresponsive.
Swing provides SwingWorker for background processing.
1 | SwingWorker<Double, Void> worker =new SwingWorker<>() { |
The two most important methods are:
doInBackground()runs outside the EDT.done()runs on the EDT after the task has completed.
SwingWorker is suitable for:
- Reading large files
- Importing data
- Exporting reports
- Calling remote APIs
- Querying databases
- Running complex calculations
- Loading large tables
Swing Development Best Practices
Use the Event Dispatch Thread (EDT)
Create and update Swing components on the EDT.
1 | SwingUtilities.invokeLater(() -> { |
Avoid Null Layout
The following approach is not recommended:
1 | panel.setLayout(null); |
Absolute positions may fail when:
- The window is resized
- The font size changes
- The application runs on another operating system
- Text labels become longer
- Display scaling changes
Use layout managers instead.
1 | panel.setLayout(new BorderLayout()); |
Keep Business Logic Outside Event Listeners
Avoid writing all logic directly inside an ActionListener.
Not recommended:
1 | button.addActionListener(event -> { |
Recommended:
1 | button.addActionListener(event -> calculateRating()); |
Then split the work into smaller methods.
1 | private void handleCalculation() { |
Use Clear Component Names
Recommended names:
- floorAreaField
- calculateButton
- heatingSystemComboBox
- ratingResultLabel
- historyTable
Avoid unclear names:
- text1
- button2
- combo
- label3
- table1
Use Constants Instead of Magic Numbers
Not recommended:1
2
3annualUsage = floorArea * 120;
annualUsage *= 0.75;
annualUsage -= 1800;
Recommended:1
2
3
4
5private static final double BASE_USAGE_PER_SQUARE_METRE = 120;
private static final double INSULATION_FACTOR = 0.75;
private static final double SOLAR_REDUCTION = 1800;
Then use the constants:1
annualUsage = floorArea * BASE_USAGE_PER_SQUARE_METRE;



