Java Swing UI Components: A Comprehensive Technical Guide

Introduction to Swing and MVC Architecture

The Java Swing library provides a comprehensive set of graphical user interface components for building desktop applications. Understanding the architectural pattern behind these components is essential for creating maintainable and flexible GUI code. The Model-View-Controller (MVC) design pattern forms the foundation of Swing's component architecture, separating data management from presentation and user interaction.

In the MVC paradigm, the Model represents the underlying data and state of a component. The View handles the visual representation of that data, while the Controller manages user interactions and updates the model accordingly. Swing implements a modified version of this pattern, often referred to as the Model-Delegate pattern, where the controller and view responsibilities are combined into a single delegate object. This separation allows multiple views of the same data and enables components to be customized without modifying their underlying behavior.

Layout Managers: Organizing Components Effectively

Layout managers are fundamental to Java GUI programming, determining the position and size of components within containers. The java.awt.LayoutManager interface defines the contract that all layout managers must fulfill, providing a layer of abstraction that makes applications more portable across different platforms and screen resolutions.

Each container in Swing has a default layout manager associated with it. When adding components to a container, the layout manager controls their arrangement according to specific rules. Five primary layout managers exist in the java.awt package, each implementing a distinct positioning strategy.

FlowLayout

FlowLayout arranges components in a directional flow, similar to text wrapping in a document. Components are positioned left-to-right and wrap to the next line when space runs out. This layout manager centers components by default and maintains their preferred sizes. Both Applet and Panel classes use FlowLayout as their default layout manager, making it suitable for simple button bars and tool palettes where components should resize gracefully.

BorderLayout

BorderLayout divides a container into five regions: North, South, East, West, and Center. The North and South regions stretch horizontally across the container, while East and West regions stretch vertically. The Center region expands to fill any remaining space. This layout is ideal for applications with a primary content area surrounded by navigation or control panels. Window, Frame, and Dialog classes default to BorderLayout, making it the foundation for most main application windows.

GridLayout

GridLayout organizes components into a rectangular grid of cells. All cells are uniform in size, determined by dividing the container dimensions by the number of rows and columns. Components fill their cells entirely, making this layout suitable for calculator interfaces, numeric keypads, and grid-based control panels. The constructor takes row and column counts as parameters, creating a predictable and consistent arrangement.

GridBagLayout

GridBagLayout is the most flexible and powerful built-in layout manager, positioning components in a grid with individual cell spanning capabilities. Components can span multiple rows or columns, and each component can have unique alignment and weight characteristics. The GridBagConstraints class configures placement rules, including anchor positions, fill behavior, padding, and external spacing. This complexity enables sophisticated layouts while maintaining responsiveness to container size changes.

CardLayout

CardLayout manages multiple components as a stack of cards, where only one card is visible at any time. This layout is useful for wizard-style dialogs, tabbed interfaces, and situations where different views need to be swapped dynamically. The layout manager provides methods to show specific cards by name or iterate through the card sequence programmatically.

Text Input Components

Swing provides several text input components designed for different use cases, from simple single-line fields to multi-line text areas with scrolling capabilities.

JTextField accepts single-line text input and supports features like placeholder text, input validation, and action triggering when the user presses Enter. JPasswordField extends JTextField but masks character input, making it suitable for password entry fields. JTextArea handles multi-line text input without built-in scrolling, making it appropriate for comments, descriptions, and other free-form text entry scenarios.

For scenarios requiring scrollable text content, JTextArea can be wrapped in a JScrollPane, which provides horizontal and vertical scrollbars as needed. JLabel displays read-only text or images and often serves as a caption for other input components, establishing visual associations between descriptive text and corresponding input fields.

Selection Components

Checkboxes and Radio Buttons

JCheckBox allows users to toggle options on or off independently, with each checkbox maintaining its own state. The component displays a box that fills with a checkmark when selected. Checkboxes are appropriate for multiple-selection scenarios where choices are not mutually exclusive.

JRadioButton provides mutually exclusive selection within a group. When radio buttons share a ButtonGroup, selecting one button automatically deselects others in the group. This behavior makes radio buttons ideal for single-choice scenarios where users must select exactly one option from a set, such as selecting a payment method or choosing a font style.

Combo Boxes

JComboBox combines a drop-down list with optional editable text input. Users can either select from the predefined list or type custom values when editable mode is enabled. Combo boxes save screen space compared to displaying all options simultaneously, making them suitable for forms with numerous selection options.

Sliders

JSlider allows users to select values from a numeric range by dragging a knob along a track. Sliders support major and minor tick marks, snap-to-tick behavior for discrete value selection, inverted ranges, and custom labeling with text, numbers, or icons. These components excel for settings where users need to adjust numeric values within defined ranges, such as volume controls, opacity settings, or timeout durations.

Menus and Toolbars

Menu components provide hierarchical navigation structures for accessing application functions. JMenuBar hosts top-level menus horizontally, JMenu contains menu items and nested submenus, and JMenuItem represents individual selectable commands. Menus support keyboard mnemonics for quick access, accelerators for global keyboard shortcuts, and can include icons, checkboxes, and radio buttons as menu items.

Popup menus, created with JPopupMenu, appear contextually when users right-click or perform equivalent gestures. These menus provide context-sensitive options relevant to the current selection or cursor position.

JToolBar provides a container for frequently-used commands represented by buttons with icons. Toolbars can be dragged to different edges of their container, offering users customizable access to common operations. Tooltips, set via setToolTipText, provide contextual help when users hover over toolbar buttons.

Dialogs and Custom Windows

Dialog windows supplement main application frames with modal or modeless secondary windows. Modal dialogs block input to other application windows until dismissed, making them appropriate for critical queries and important information display. Modeless dialogs allow users to interact with other windows simultaneously, suitable for palette windows and reference materials.

JOptionPane provides static methods for common dialog patterns, including message dialogs for information display, confirm dialogs for yes/no/cancel decisions, input dialogs for value collection, and option dialogs for custom button configurations. These pre-built dialogs handle sizing, layout, and accessibility automatically.

For custom dialog requirements, JDialog can be extended directly, allowing arbitrary content and behavior. Dialogs often implement data exchange patterns where initial values are set before display, and results are retrieved after dismissal. The JColorChooser component provides a standardized interface for color selection, supporting modal, modeless, and immediate update modes.

Code Examples

Calculator Implementation Using GridLayout

The following example demonstrates a simple calculator interface built with Swing components. The calculator uses BorderLayout to structure its display and button panel, with GridLayout arranging buttons in a grid pattern. Event handling is implemented through action listeners that process button presses and update the display accordingly.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CalculatorDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            CalculatorWindow window = new CalculatorWindow();
            window.setTitle("Calculator");
            window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            window.setVisible(true);
        });
    }
}

class CalculatorWindow extends JFrame {
    public CalculatorWindow() {
        add(new CalculatorPanel());
        pack();
    }
}

class CalculatorPanel extends JPanel {
    private JButton outputDisplay;
    private JPanel buttonGrid;
    private double accumulatedResult;
    private String previousOperator;
    private boolean startingNewNumber;

    public CalculatorPanel() {
        setLayout(new BorderLayout());
        
        accumulatedResult = 0;
        previousOperator = "=";
        startingNewNumber = true;
        
        outputDisplay = new JButton("0");
        outputDisplay.setEnabled(false);
        add(outputDisplay, BorderLayout.NORTH);
        
        ActionListener digitHandler = new DigitEntryAction();
        ActionListener operatorHandler = new OperatorAction();
        
        buttonGrid = new JPanel();
        buttonGrid.setLayout(new GridLayout(4, 4));
        
        addCalculatorButton("7", digitHandler);
        addCalculatorButton("8", digitHandler);
        addCalculatorButton("9", digitHandler);
        addCalculatorButton("/", operatorHandler);
        
        addCalculatorButton("4", digitHandler);
        addCalculatorButton("5", digitHandler);
        addCalculatorButton("6", digitHandler);
        addCalculatorButton("*", operatorHandler);
        
        addCalculatorButton("1", digitHandler);
        addCalculatorButton("2", digitHandler);
        addCalculatorButton("3", digitHandler);
        addCalculatorButton("-", operatorHandler);
        
        addCalculatorButton("0", digitHandler);
        addCalculatorButton(".", digitHandler);
        addCalculatorButton("=", operatorHandler);
        addCalculatorButton("+", operatorHandler);
        
        add(buttonGrid, BorderLayout.SOUTH);
    }
    
    private void addCalculatorButton(String label, ActionListener handler) {
        JButton btn = new JButton(label);
        btn.addActionListener(handler);
        buttonGrid.add(btn);
    }
    
    private class DigitEntryAction implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            String input = event.getActionCommand();
            if (startingNewNumber) {
                outputDisplay.setText("");
                startingNewNumber = false;
            }
            outputDisplay.setText(outputDisplay.getText() + input);
        }
    }
    
    private class OperatorAction implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            String operation = event.getActionCommand();
            
            if (startingNewNumber) {
                if (operation.equals("-")) {
                    outputDisplay.setText(operation);
                    startingNewNumber = false;
                } else {
                    previousOperator = operation;
                }
            } else {
                performCalculation(Double.parseDouble(outputDisplay.getText()));
                previousOperator = operation;
                startingNewNumber = true;
            }
        }
    }
    
    public void performCalculation(double operand) {
        switch (previousOperator) {
            case "+": accumulatedResult += operand; break;
            case "-": accumulatedResult -= operand; break;
            case "*": accumulatedResult *= operand; break;
            case "/": accumulatedResult /= operand; break;
            case "=": accumulatedResult = operand; break;
        }
        outputDisplay.setText(String.valueOf(accumulatedResult));
    }
}

Text Component Demonstration

This example illustrates the use of various text input components, including text fields, password fields, and text areas with scroll support. The layout uses GridLayout for the input section and BorderLayout for overall panel organization.


import java.awt.*;
import javax.swing.*;

public class TextFieldDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new TextInputFrame();
            frame.setTitle("TextComponentDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class TextInputFrame extends JFrame {
    private static final int TEXT_AREA_ROWS = 8;
    private static final int TEXT_AREA_COLUMNS = 20;
    
    public TextInputFrame() {
        JTextField usernameField = new JTextField();
        JPasswordField passwordField = new JPasswordField();
        
        JPanel inputSection = new JPanel();
        inputSection.setLayout(new GridLayout(2, 2));
        inputSection.add(new JLabel("Username: ", SwingConstants.RIGHT));
        inputSection.add(usernameField);
        inputSection.add(new JLabel("Password: ", SwingConstants.RIGHT));
        inputSection.add(passwordField);
        
        add(inputSection, BorderLayout.NORTH);
        
        JTextArea textOutput = new JTextArea(TEXT_AREA_ROWS, TEXT_AREA_COLUMNS);
        JScrollPane scrollContainer = new JScrollPane(textOutput);
        
        add(scrollContainer, BorderLayout.CENTER);
        
        JPanel actionPanel = new JPanel();
        JButton appendButton = new JButton("Append Details");
        actionPanel.add(appendButton);
        
        appendButton.addActionListener(event -> {
            textOutput.append("Username: " + usernameField.getText() + 
                " Password: " + new String(passwordField.getPassword()) + "\n");
        });
        
        add(actionPanel, BorderLayout.SOUTH);
        pack();
    }
}

Checkbox Selection Demo

Checkboxes enable binary state selection, and this example demonstrates using checkboxes to control font attributes. The checkboxes share an action listener that dynamically updates label formatting based on their combined state.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CheckboxDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new CheckboxFrame();
            frame.setTitle("CheckboxDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class CheckboxFrame extends JFrame {
    private JLabel sampleText;
    private JCheckBox boldCheckbox;
    private JCheckBox italicCheckbox;
    private static final int FONT_SIZE = 24;
    
    public CheckboxFrame() {
        sampleText = new JLabel("The quick brown fox jumps over the lazy dog.");
        sampleText.setFont(new Font("Serif", Font.BOLD, FONT_SIZE));
        add(sampleText, BorderLayout.CENTER);
        
        ActionListener styleUpdater = event -> {
            int styleFlags = 0;
            if (boldCheckbox.isSelected()) styleFlags += Font.BOLD;
            if (italicCheckbox.isSelected()) styleFlags += Font.ITALIC;
            sampleText.setFont(new Font("Serif", styleFlags, FONT_SIZE));
        };
        
        JPanel checkboxPanel = new JPanel();
        
        boldCheckbox = new JCheckBox("Bold");
        boldCheckbox.addActionListener(styleUpdater);
        boldCheckbox.setSelected(true);
        checkboxPanel.add(boldCheckbox);
        
        italicCheckbox = new JCheckBox("Italic");
        italicCheckbox.addActionListener(styleUpdater);
        checkboxPanel.add(italicCheckbox);
        
        add(checkboxPanel, BorderLayout.SOUTH);
        pack();
    }
}

Radio Button Implementation

Radio buttons enforce mutual exclusivity within a button group. This example shows using radio buttons to select font sizes, demonstrating how to manage group selection state and update component properties accordingly.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class RadioButtonDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new RadioButtonFrame();
            frame.setTitle("RadioButtonDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class RadioButtonFrame extends JFrame {
    private JPanel buttonGroup;
    private ButtonGroup radioGroup;
    private JLabel previewLabel;
    private static final int DEFAULT_FONT_SIZE = 36;
    
    public RadioButtonFrame() {
        previewLabel = new JLabel("The quick brown fox jumps over the lazy dog.");
        previewLabel.setFont(new Font("Serif", Font.PLAIN, DEFAULT_FONT_SIZE));
        add(previewLabel, BorderLayout.CENTER);
        
        buttonGroup = new JPanel();
        radioGroup = new ButtonGroup();
        
        addRadioOption("Small", 8);
        addRadioOption("Medium", 12);
        addRadioOption("Large", 18);
        addRadioOption("Extra Large", 36);
        
        add(buttonGroup, BorderLayout.SOUTH);
        pack();
    }
    
    public void addRadioOption(String label, int fontSize) {
        boolean isSelected = fontSize == DEFAULT_FONT_SIZE;
        JRadioButton option = new JRadioButton(label, isSelected);
        radioGroup.add(option);
        buttonGroup.add(option);
        
        ActionListener sizeChanger = event -> 
            previewLabel.setFont(new Font("Serif", Font.PLAIN, fontSize));
        
        option.addActionListener(sizeChanger);
    }
}

Custom Border Styles

The BorderFactory class provides various border styles for enhancing component appearance. This example demonstrates different border types and uses radio buttons to switch between them dynamically.


import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

public class BorderDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new BorderSelectionFrame();
            frame.setTitle("BorderDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class BorderSelectionFrame extends JFrame {
    private JPanel previewPanel;
    private JPanel optionPanel;
    private ButtonGroup styleGroup;
    
    public BorderSelectionFrame() {
        previewPanel = new JPanel();
        optionPanel = new JPanel();
        styleGroup = new ButtonGroup();
        
        addBorderOption("Lowered Bevel", BorderFactory.createLoweredBevelBorder());
        addBorderOption("Raised Bevel", BorderFactory.createRaisedBevelBorder());
        addBorderOption("Etched", BorderFactory.createEtchedBorder());
        addBorderOption("Line Border", BorderFactory.createLineBorder(Color.BLUE));
        addBorderOption("Matte Border", BorderFactory.createMatteBorder(15, 15, 15, 15, Color.BLUE));
        addBorderOption("Empty", BorderFactory.createEmptyBorder());
        
        Border etchedStyle = BorderFactory.createEtchedBorder();
        Border titled = BorderFactory.createTitledBorder(etchedStyle, "Available Styles");
        optionPanel.setBorder(titled);
        
        setLayout(new GridLayout(2, 1));
        add(optionPanel);
        add(previewPanel);
        pack();
    }
    
    public void addBorderOption(String buttonLabel, Border borderStyle) {
        JRadioButton option = new JRadioButton(buttonLabel);
        option.addActionListener(event -> previewPanel.setBorder(borderStyle));
        styleGroup.add(option);
        optionPanel.add(option);
    }
}

Font Selection with ComboBox

Combo boxes provide compact selection interfaces. This example uses a combo box to select font families, with the preview label updating dynamically to show the selected font.


import java.awt.*;
import javax.swing.*;

public class FontSelectionDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new FontSelectionFrame();
            frame.setTitle("FontSelectionDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class FontSelectionFrame extends JFrame {
    private JComboBox<string> fontSelector;
    private JLabel previewLabel;
    private static final int DEFAULT_SIZE = 24;
    
    public FontSelectionFrame() {
        previewLabel = new JLabel("The quick brown fox jumps over the lazy dog.");
        previewLabel.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE));
        add(previewLabel, BorderLayout.CENTER);
        
        fontSelector = new JComboBox<>();
        fontSelector.addItem("Serif");
        fontSelector.addItem("SansSerif");
        fontSelector.addItem("Monospaced");
        fontSelector.addItem("Dialog");
        fontSelector.addItem("DialogInput");
        
        fontSelector.addActionListener(event ->
            previewLabel.setFont(
                new Font((String)fontSelector.getItemAt(fontSelector.getSelectedIndex()), 
                    Font.PLAIN, DEFAULT_SIZE)));
        
        JPanel comboPanel = new JPanel();
        comboPanel.add(fontSelector);
        add(comboPanel, BorderLayout.SOUTH);
        pack();
    }
}
</string>

Slider Configuration Examples

Sliders provide intuitive numeric input through draggable knobs. This comprehensive example demonstrates various slider configurations including tick marks, snapping behavior, inverted ranges, and custom labeling.


import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

public class SliderDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new SliderFrame();
            frame.setTitle("SliderDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class SliderFrame extends JFrame {
    private JPanel sliderContainer;
    private JTextField valueDisplay;
    private ChangeListener sliderListener;
    
    public SliderFrame() {
        sliderContainer = new JPanel();
        sliderContainer.setLayout(new GridBagLayout());
        
        sliderListener = event -> {
            JSlider source = (JSlider) event.getSource();
            valueDisplay.setText(String.valueOf(source.getValue()));
        };
        
        JSlider plainSlider = new JSlider();
        addSlider(plainSlider, "Standard Slider");
        
        JSlider tickSlider = new JSlider();
        tickSlider.setPaintTicks(true);
        tickSlider.setMajorTickSpacing(20);
        tickSlider.setMinorTickSpacing(5);
        addSlider(tickSlider, "With Tick Marks");
        
        JSlider snapSlider = new JSlider();
        snapSlider.setPaintTicks(true);
        snapSlider.setSnapToTicks(true);
        snapSlider.setMajorTickSpacing(20);
        snapSlider.setMinorTickSpacing(5);
        addSlider(snapSlider, "Snap to Ticks");
        
        JSlider noTrackSlider = new JSlider();
        noTrackSlider.setPaintTicks(true);
        noTrackSlider.setMajorTickSpacing(20);
        noTrackSlider.setMinorTickSpacing(5);
        noTrackSlider.setPaintTrack(false);
        addSlider(noTrackSlider, "No Track");
        
        JSlider invertedSlider = new JSlider();
        invertedSlider.setPaintTicks(true);
        invertedSlider.setMajorTickSpacing(20);
        invertedSlider.setMinorTickSpacing(5);
        invertedSlider.setInverted(true);
        addSlider(invertedSlider, "Inverted Direction");
        
        JSlider numberedSlider = new JSlider();
        numberedSlider.setPaintTicks(true);
        numberedSlider.setPaintLabels(true);
        numberedSlider.setMajorTickSpacing(20);
        numberedSlider.setMinorTickSpacing(5);
        addSlider(numberedSlider, "Number Labels");
        
        JSlider letterSlider = new JSlider();
        letterSlider.setPaintTicks(true);
        letterSlider.setPaintLabels(true);
        letterSlider.setMajorTickSpacing(20);
        letterSlider.setMinorTickSpacing(5);
        
        Dictionary<integer component=""> letterLabels = new Hashtable<>();
        letterLabels.put(0, new JLabel("A"));
        letterLabels.put(20, new JLabel("B"));
        letterLabels.put(40, new JLabel("C"));
        letterLabels.put(60, new JLabel("D"));
        letterLabels.put(80, new JLabel("E"));
        letterLabels.put(100, new JLabel("F"));
        
        letterSlider.setLabelTable(letterLabels);
        addSlider(letterSlider, "Custom Letter Labels");
        
        valueDisplay = new JTextField();
        add(sliderContainer, BorderLayout.CENTER);
        add(valueDisplay, BorderLayout.SOUTH);
        pack();
    }
    
    public void addSlider(JSlider slider, String description) {
        slider.addChangeListener(sliderListener);
        JPanel wrapper = new JPanel();
        wrapper.add(slider);
        wrapper.add(new JLabel(description));
        wrapper.setAlignmentX(Component.LEFT_ALIGNMENT);
        GridBagConstraints constraints = new GridBagConstraints();
        constraints.gridy = sliderContainer.getComponentCount();
        constraints.anchor = GridBagConstraints.WEST;
        sliderContainer.add(wrapper, constraints);
    }
}
</integer>

Menu System Implementation

Menus provide hierarchical command organization with keyboard shortcuts and mnemonic access. This example demonstrates menu creation, accelerator keys, checkbox and radio button menu items, and popup menus.


import java.awt.event.*;
import javax.swing.*;

public class MenuSystemDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new MenuFrame();
            frame.setTitle("MenuSystemDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class MenuFrame extends JFrame {
    private static final int FRAME_WIDTH = 300;
    private static final int FRAME_HEIGHT = 200;
    private Action saveCommand;
    private Action saveAsCommand;
    private JCheckBoxMenuItem readOnlyOption;
    private JPopupMenu contextMenu;
    
    class CommandAction extends AbstractAction {
        public CommandAction(String title) {
            super(title);
        }
        
        public void actionPerformed(ActionEvent event) {
            System.out.println(getValue(Action.NAME) + " activated.");
        }
    }
    
    public MenuFrame() {
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        
        JMenu fileMenu = new JMenu("File");
        fileMenu.add(new CommandAction("New"));
        
        JMenuItem openOption = fileMenu.add(new CommandAction("Open"));
        openOption.setAccelerator(KeyStroke.getKeyStroke("control O"));
        
        fileMenu.addSeparator();
        
        saveCommand = new CommandAction("Save");
        JMenuItem saveOption = fileMenu.add(saveCommand);
        saveOption.setAccelerator(KeyStroke.getKeyStroke("control S"));
        
        saveAsCommand = new CommandAction("Save As");
        fileMenu.add(saveAsCommand);
        fileMenu.addSeparator();
        
        fileMenu.add(new AbstractAction("Exit") {
            public void actionPerformed(ActionEvent event) {
                System.exit(0);
            }
        });
        
        readOnlyOption = new JCheckBoxMenuItem("Read-Only Mode");
        readOnlyOption.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                boolean canSave = !readOnlyOption.isSelected();
                saveCommand.setEnabled(canSave);
                saveAsCommand.setEnabled(canSave);
            }
        });
        
        ButtonGroup modeGroup = new ButtonGroup();
        JRadioButtonMenuItem insertMode = new JRadioButtonMenuItem("Insert Mode");
        insertMode.setSelected(true);
        JRadioButtonMenuItem overwriteMode = new JRadioButtonMenuItem("Overwrite Mode");
        
        modeGroup.add(insertMode);
        modeGroup.add(overwriteMode);
        
        Action cutCommand = new CommandAction("Cut");
        cutCommand.putValue(Action.SMALL_ICON, new ImageIcon("cut-icon.gif"));
        Action copyCommand = new CommandAction("Copy");
        copyCommand.putValue(Action.SMALL_ICON, new ImageIcon("copy-icon.gif"));
        Action pasteCommand = new CommandAction("Paste");
        pasteCommand.putValue(Action.SMALL_ICON, new ImageIcon("paste-icon.gif"));
        
        JMenu editMenu = new JMenu("Edit");
        editMenu.add(cutCommand);
        editMenu.add(copyCommand);
        editMenu.add(pasteCommand);
        
        JMenu preferencesMenu = new JMenu("Preferences");
        preferencesMenu.add(readOnlyOption);
        preferencesMenu.addSeparator();
        preferencesMenu.add(insertMode);
        preferencesMenu.add(overwriteMode);
        
        editMenu.addSeparator();
        editMenu.add(preferencesMenu);
        
        JMenu helpMenu = new JMenu("Help");
        helpMenu.setMnemonic('H');
        
        JMenuItem indexOption = new JMenuItem("Index");
        indexOption.setMnemonic('I');
        helpMenu.add(indexOption);
        
        Action aboutCommand = new CommandAction("About");
        aboutCommand.putValue(Action.MNEMONIC_KEY, new Integer('A'));
        helpMenu.add(aboutCommand);
        
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);
        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        menuBar.add(helpMenu);
        
        contextMenu = new JPopupMenu();
        contextMenu.add(cutCommand);
        contextMenu.add(copyCommand);
        contextMenu.add(pasteCommand);
        
        JPanel contentPanel = new JPanel();
        contentPanel.setComponentPopupMenu(contextMenu);
        add(contentPanel);
    }
}

Custom Layout Manager: Circular Component Arrangement

Custom layout managers can implement unique positioning algorithms. This example shows a CircleLayout that arranges components along a circular path, demonstrating the LayoutManager interface implementation.


import java.awt.*;

public class CircularLayoutDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new CircularLayoutFrame();
            frame.setTitle("CircularLayoutDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class CircularLayoutFrame extends JFrame {
    public CircularLayoutFrame() {
        setLayout(new CircularComponentLayout());
        add(new JButton("Yellow"));
        add(new JButton("Blue"));
        add(new JButton("Red"));
        add(new JButton("Green"));
        add(new JButton("Orange"));
        add(new JButton("Purple"));
        add(new JButton("Cyan"));
        pack();
    }
}

class CircularComponentLayout implements LayoutManager {
    private int minimumWidth;
    private int minimumHeight;
    private int preferredWidth;
    private int preferredHeight;
    private boolean dimensionsCalculated;
    private int maximumComponentWidth;
    private int maximumComponentHeight;
    
    public void addLayoutComponent(String identifier, Component component) {
    }
    
    public void removeLayoutComponent(Component component) {
    }
    
    public void calculateDimensions(Container parent) {
        if (dimensionsCalculated) return;
        
        int componentCount = parent.getComponentCount();
        preferredWidth = 0;
        preferredHeight = 0;
        minimumWidth = 0;
        minimumHeight = 0;
        maximumComponentWidth = 0;
        maximumComponentHeight = 0;
        
        for (int index = 0; index < componentCount; index++) {
            Component current = parent.getComponent(index);
            if (current.isVisible()) {
                Dimension size = current.getPreferredSize();
                maximumComponentWidth = Math.max(maximumComponentWidth, size.width);
                maximumComponentHeight = Math.max(maximumComponentHeight, size.height);
                preferredWidth += size.width;
                preferredHeight += size.height;
            }
        }
        
        minimumWidth = preferredWidth / 2;
        minimumHeight = preferredHeight / 2;
        dimensionsCalculated = true;
    }
    
    public Dimension preferredLayoutSize(Container parent) {
        calculateDimensions(parent);
        Insets margins = parent.getInsets();
        int totalWidth = preferredWidth + margins.left + margins.right;
        int totalHeight = preferredHeight + margins.top + margins.bottom;
        return new Dimension(totalWidth, totalHeight);
    }
    
    public Dimension minimumLayoutSize(Container parent) {
        calculateDimensions(parent);
        Insets margins = parent.getInsets();
        int totalWidth = minimumWidth + margins.left + margins.right;
        int totalHeight = minimumHeight + margins.top + margins.bottom;
        return new Dimension(totalWidth, totalHeight);
    }
    
    public void layoutContainer(Container parent) {
        calculateDimensions(parent);
        
        Insets margins = parent.getInsets();
        int availableWidth = parent.getSize().width - margins.left - margins.right;
        int availableHeight = parent.getSize().height - margins.top - margins.bottom;
        
        int centerX = margins.left + availableWidth / 2;
        int centerY = margins.top + availableHeight / 2;
        
        int horizontalRadius = (availableWidth - maximumComponentWidth) / 2;
        int verticalRadius = (availableHeight - maximumComponentHeight) / 2;
        int radius = Math.min(horizontalRadius, verticalRadius);
        
        int componentCount = parent.getComponentCount();
        for (int index = 0; index < componentCount; index++) {
            Component current = parent.getComponent(index);
            if (current.isVisible()) {
                double angle = 2 * Math.PI * index / componentCount;
                
                int positionX = centerX + (int) (Math.cos(angle) * radius);
                int positionY = centerY + (int) (Math.sin(angle) * radius);
                
                Dimension componentSize = current.getPreferredSize();
                current.setBounds(
                    positionX - componentSize.width / 2,
                    positionY - componentSize.height / 2,
                    componentSize.width,
                    componentSize.height
                );
            }
        }
    }
}

Dialog Examples

Dialog windows provide focused interaction for alerts, confirmations, and data collection. The following examples demonstrate message dialogs, custom dialogs, and data exchange patterns.


import java.awt.*;
import javax.swing.*;

public class DialogExamples {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new DialogDemoFrame();
            frame.setTitle("DialogExamples");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class DialogDemoFrame extends JFrame {
    private static final int FRAME_WIDTH = 300;
    private static final int FRAME_HEIGHT = 200;
    private AboutWindow infoDialog;
    
    public DialogDemoFrame() {
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);
        JMenu fileMenu = new JMenu("File");
        menuBar.add(fileMenu);
        
        JMenuItem aboutOption = new JMenuItem("About");
        aboutOption.addActionListener(event -> {
            if (infoDialog == null) {
                infoDialog = new AboutWindow(DialogDemoFrame.this);
            }
            infoDialog.setVisible(true);
        });
        fileMenu.add(aboutOption);
        
        JMenuItem exitOption = new JMenuItem("Exit");
        exitOption.addActionListener(event -> System.exit(0));
        fileMenu.add(exitOption);
    }
}

class AboutWindow extends JDialog {
    public AboutWindow(JFrame parent) {
        super(parent, "Application Information", true);
        
        add(new JLabel("<h1><i>Swing Components</i></h1><hr></hr>Java GUI Library"),
            BorderLayout.CENTER);
        
        JButton closeButton = new JButton("OK");
        closeButton.addActionListener(event -> setVisible(false));
        
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(closeButton);
        add(buttonPanel, BorderLayout.SOUTH);
        
        pack();
    }
}

Color Chooser Implementation

The JColorChooser component provides sophisticated color selection capabilities. This example demonstrates modal, modeless, and immediate-update color chooser patterns.


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ColorChooserDemo {
    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new ColorFrame();
            frame.setTitle("ColorChooserDemo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }
}

class ColorFrame extends JFrame {
    private static final int FRAME_WIDTH = 400;
    private static final int FRAME_HEIGHT = 300;
    
    public ColorFrame() {
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        
        ColorPanel colorPanel = new ColorPanel();
        add(colorPanel);
    }
}

class ColorPanel extends JPanel {
    public ColorPanel() {
        JButton modalButton = new JButton("Modal Chooser");
        modalButton.addActionListener(new ModalHandler());
        add(modalButton);
        
        JButton modelessButton = new JButton("Modeless Chooser");
        modelessButton.addActionListener(new ModelessHandler());
        add(modelessButton);
        
        JButton immediateButton = new JButton("Immediate Updates");
        immediateButton.addActionListener(new ImmediateHandler());
        add(immediateButton);
    }
    
    private class ModalHandler implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            Color current = getBackground();
            Color selected = JColorChooser.showDialog(ColorPanel.this, 
                "Background Selection", current);
            if (selected != null) {
                setBackground(selected);
            }
        }
    }
    
    private class ModelessHandler implements ActionListener {
        private JDialog dialogWindow;
        private JColorChooser colorSelector;
        
        public ModelessHandler() {
            colorSelector = new JColorChooser();
            dialogWindow = JColorChooser.createDialog(ColorPanel.this,
                "Background Color", false, colorSelector,
                event -> setBackground(colorSelector.getColor()),
                null);
        }
        
        public void actionPerformed(ActionEvent event) {
            colorSelector.setColor(getBackground());
            dialogWindow.setVisible(true);
        }
    }
    
    private class ImmediateHandler implements ActionListener {
        private JDialog dialogWindow;
        private JColorChooser colorSelector;
        
        public ImmediateHandler() {
            colorSelector = new JColorChooser();
            colorSelector.getSelectionModel().addChangeListener(
                event -> setBackground(colorSelector.getColor()));
            
            dialogWindow = new JDialog((Frame) null, "Color Selection", false);
            dialogWindow.add(colorSelector);
            dialogWindow.pack();
        }
        
        public void actionPerformed(ActionEvent event) {
            colorSelector.setColor(getBackground());
            dialogWindow.setVisible(true);
        }
    }
}

Conclusion

This comprehensive guide covers the essential aspects of Java Swing UI development, from fundamental layout management principles to advanced component interactions and dialog creation. Understanding these concepts enables developers to create sophisticated, user-friendly desktop applications with Java's mature and feature-rich GUI toolkit.

Tags: java swing gui layout-manager awt

Posted on Thu, 11 Jun 2026 16:47:07 +0000 by Piba