1. Swing Components

Swing is a Java library for creating platform-independent GUIs, part of the Java Foundation Classes (JFC) in the javax.swing package. Swing components are lightweight (drawn by Java, not native OS) and highly customizable.

  • Key Characteristics:

    • Built on top of AWT (Abstract Window Toolkit) but more flexible and feature-rich.
    • Components are in javax.swing (e.g., JFrame, JButton, JLabel).
    • Platform-independent, with customizable look and feel.
    • Supports MVC (Model-View-Controller) architecture for separating data and UI.
  • Common Swing Components:

    • Containers:
      • JFrame: Top-level window with a title bar and borders.
      • JPanel: General-purpose container for grouping components.
      • JDialog: Pop-up dialog for user interaction.
    • Basic Controls:
      • JButton: Clickable button.
      • JLabel: Displays text or images.
      • JTextField: Single-line text input.
      • JTextArea: Multi-line text input.
      • JCheckBox, JRadioButton: Selection options.
      • JComboBox: Dropdown list.
    • Complex Components:
      • JTable: Displays tabular data.
      • JTree: Displays hierarchical data.
      • JList: Displays a list of items.
    • Menus:
      • JMenuBar, JMenu, JMenuItem: For menu systems.
  • Example:

    package com.example.myapp;
    import javax.swing.*;
    public class SwingDemo {
        public static void main(String[] args) {
            JFrame frame = new JFrame("Swing Demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(300, 200);
            // Add components
            JLabel label = new JLabel("Hello, Swing!");
            JButton button = new JButton("Click Me");
            JTextField textField = new JTextField(10);
            JPanel panel = new JPanel();
            panel.add(label);
            panel.add(textField);
            panel.add(button);
            frame.add(panel);
            frame.setVisible(true);
        }
    }
  • Key Points:

    • Always set JFrame.setDefaultCloseOperation() (e.g., EXIT_ON_CLOSE).
    • Use setVisible(true) to display the frame.
    • Components must be added to a container (e.g., JPanel or JFrame’s content pane).
    • Swing components throw unchecked exceptions (e.g., NullPointerException) if misconfigured.

2. Laying Out Components in a Container

Layout managers control the positioning and sizing of components within a container (e.g., JPanel, JFrame). Swing provides several layout managers to organize components flexibly.

  • Common Layout Managers:

    • FlowLayout:
      • Arranges components in a row, wrapping to the next line if needed.
      • Default for JPanel.
      • Example:
        panel.setLayout(new FlowLayout());
        panel.add(new JButton("Button 1"));
        panel.add(new JButton("Button 2"));
    • BorderLayout:
      • Divides the container into five regions: NORTH, SOUTH, EAST, WEST, CENTER.
      • Default for JFrame’s content pane.
      • Example:
        frame.setLayout(new BorderLayout());
        frame.add(new JButton("North"), BorderLayout.NORTH);
        frame.add(new JButton("Center"), BorderLayout.CENTER);
    • GridLayout:
      • Arranges components in a grid (equal-sized cells).
      • Example:
        panel.setLayout(new GridLayout(2, 2));
        panel.add(new JButton("1"));
        panel.add(new JButton("2"));
        panel.add(new JButton("3"));
        panel.add(new JButton("4"));
    • BoxLayout:
      • Arranges components in a single row or column.
      • Example:
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        panel.add(new JButton("Top"));
        panel.add(new JButton("Bottom"));
    • GridBagLayout:
      • Highly flexible, grid-based layout with customizable constraints.
      • Example:
        panel.setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        panel.add(new JButton("Button"), gbc);
  • Example (Combining Layouts):

    package com.example.myapp;
    import javax.swing.*;
    import java.awt.*;
    public class LayoutDemo {
        public static void main(String[] args) {
            JFrame frame = new JFrame("Layout Demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 300);
            // BorderLayout for frame
            frame.setLayout(new BorderLayout());
            // Panel with FlowLayout
            JPanel topPanel = new JPanel(new FlowLayout());
            topPanel.add(new JLabel("Top"));
            topPanel.add(new JTextField(10));
            frame.add(topPanel, BorderLayout.NORTH);
            // Panel with GridLayout
            JPanel centerPanel = new JPanel(new GridLayout(2, 2));
            centerPanel.add(new JButton("1"));
            centerPanel.add(new JButton("2"));
            centerPanel.add(new JButton("3"));
            centerPanel.add(new JButton("4"));
            frame.add(centerPanel, BorderLayout.CENTER);
            frame.setVisible(true);
        }
    }
  • Key Points:

    • Set the layout manager using setLayout().
    • Use null layout for absolute positioning (not recommended, as it’s not responsive).
    • Combine layouts by nesting panels with different layout managers.
    • Layout managers handle resizing and alignment automatically.

3. Panels

Panels (JPanel) are lightweight containers used to group and organize components within a larger container (e.g., JFrame). They are versatile and support any layout manager.

  • Key Characteristics:

    • Default layout: FlowLayout.
    • Can contain other components (e.g., buttons, labels) or nested panels.
    • Used to modularize UI design (e.g., separate sections of a form).
  • Example:

    package com.example.myapp;
    import javax.swing.*;
    import java.awt.*;
    public class PanelDemo {
        public static void main(String[] args) {
            JFrame frame = new JFrame("Panel Demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(300, 200);
            // Main panel with BorderLayout
            JPanel mainPanel = new JPanel(new BorderLayout());
            // Sub-panel 1 (FlowLayout)
            JPanel topPanel = new JPanel(new FlowLayout());
            topPanel.add(new JLabel("Name:"));
            topPanel.add(new JTextField(10));
            mainPanel.add(topPanel, BorderLayout.NORTH);
            // Sub-panel 2 (GridLayout)
            JPanel buttonPanel = new JPanel(new GridLayout(1, 2));
            buttonPanel.add(new JButton("OK"));
            buttonPanel.add(new JButton("Cancel"));
            mainPanel.add(buttonPanel, BorderLayout.SOUTH);
            frame.add(mainPanel);
            frame.setVisible(true);
        }
    }
  • Key Points:

    • Use panels to break down complex UIs into manageable sections.
    • Each panel can have its own layout manager.
    • Add panels to JFrame’s content pane (frame.add(panel)).
    • Panels are transparent by default; set background with setBackground(Color).

4. Look & Feel

Look and Feel (L&F) defines the visual appearance and behavior of Swing components (e.g., colors, fonts, borders). Swing supports pluggable look and feel, allowing you to change the UI style.

  • Default Look and Feel:

    • Cross-platform (Java’s “Metal” look, or “Nimbus” in newer versions).
    • Platform-specific (e.g., Windows, macOS) via UIManager.setLookAndFeel().
  • Changing Look and Feel:

    • Use UIManager.setLookAndFeel(String className) or predefined look and feel classes.
    • Common options:
      • com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel (modern, cross-platform).
      • UIManager.getSystemLookAndFeelClassName() (native OS look).
    • Example:
      package com.example.myapp;
      import javax.swing.*;
      import java.awt.*;
      public class LookAndFeelDemo {
          public static void main(String[] args) {
              try {
                  // Set Nimbus Look and Feel
                  UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
              } catch (Exception e) {
                  System.err.println("Error setting look and feel: " + e.getMessage());
              }
              JFrame frame = new JFrame("Look and Feel Demo");
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setSize(300, 200);
              JPanel panel = new JPanel();
              panel.add(new JButton("Click Me"));
              panel.add(new JLabel("Nimbus Look"));
              frame.add(panel);
              frame.setVisible(true);
          }
      }
  • Key Points:

    • Set look and feel before creating components for consistency.
    • Handle exceptions (UnsupportedLookAndFeelException, etc.).
    • Use UIManager.getInstalledLookAndFeels() to list available L&Fs.
    • Nimbus is recommended for modern applications.

5. Event Listeners

Event listeners handle user interactions (e.g., button clicks, key presses, mouse movements) in Swing. Swing uses the event-driven programming model, where events trigger listener methods.

  • Key Concepts:

    • Event: An object (e.g., ActionEvent, MouseEvent) representing a user action.
    • Listener: An interface (e.g., ActionListener, MouseListener) defining methods to handle events.
    • Source: The component generating the event (e.g., JButton).
    • Register listeners using addXXXListener() methods (e.g., addActionListener()).
  • Common Listeners:

    • ActionListener: Handles actions (e.g., button clicks, menu selections).
      • Method: actionPerformed(ActionEvent e).
    • MouseListener: Handles mouse events (e.g., clicks, enters/exits).
      • Methods: mouseClicked(), mousePressed(), etc.
    • KeyListener: Handles keyboard events.
      • Methods: keyPressed(), keyReleased(), keyTyped().
    • WindowListener: Handles window events (e.g., closing, opening).
      • Methods: windowClosing(), windowOpened(), etc.
  • Example (ActionListener):

    package com.example.myapp;
    import javax.swing.*;
    import java.awt.event.*;
    public class EventListenerDemo {
        public static void main(String[] args) {
            JFrame frame = new JFrame("Event Demo");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(300, 200);
            JPanel panel = new JPanel();
            JButton button = new JButton("Click Me");
            JTextField textField = new JTextField(10);
            // Add ActionListener
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    textField.setText("Button Clicked!");
                }
            });
            panel.add(button);
            panel.add(textField);
            frame.add(panel);
            frame.setVisible(true);
        }
    }
  • Lambda Expression (Java 8+):

    button.addActionListener(e -> textField.setText("Button Clicked!"));
  • Key Points:

    • Listeners are interfaces; implement them using classes, anonymous classes, or lambdas.
    • Multiple listeners can be added to a component.
    • Use getSource() or getActionCommand() in ActionEvent to identify the source.
    • Handle exceptions (e.g., IllegalArgumentException) for invalid listener configurations.

6. Concurrency in Swing

Swing is not thread-safe, meaning most Swing components must be accessed from the Event Dispatch Thread (EDT), a single thread responsible for handling GUI events and updates. Concurrency in Swing ensures responsive UIs during long-running tasks.

  • Key Concepts:

    • Event Dispatch Thread (EDT):
      • Handles all GUI events (e.g., button clicks, repaints).
      • All Swing component creation and updates must occur on the EDT.
    • SwingUtilities.invokeLater():
      • Schedules code to run on the EDT.
      • Example:
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Demo");
            frame.setVisible(true);
        });
    • SwingUtilities.invokeAndWait():
      • Runs code on the EDT and waits for completion (used rarely).
    • Worker Threads:
      • Perform long-running tasks (e.g., file I/O, network calls) off the EDT to avoid freezing the UI.
      • Use SwingWorker for background tasks with UI updates.
  • SwingWorker:

    • A utility class (javax.swing.SwingWorker) for running tasks in a background thread and updating the UI on the EDT.
    • Methods:
      • doInBackground(): Runs the task in a worker thread.
      • done(): Executes on the EDT after the task completes.
      • publish()/process(): Sends intermediate results to the EDT.
  • Example (SwingWorker):

    package com.example.myapp;
    import javax.swing.*;
    import java.awt.*;
    import java.util.List;
    public class ConcurrencyDemo {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                JFrame frame = new JFrame("Concurrency Demo");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(300, 200);
                JLabel label = new JLabel("Waiting...");
                JButton button = new JButton("Start Task");
                JPanel panel = new JPanel();
                panel.add(button);
                panel.add(label);
                frame.add(panel);
                // ActionListener with SwingWorker
                button.addActionListener(e -> {
                    SwingWorker<String, String> worker = new SwingWorker<>() {
                        @Override
                        protected String doInBackground() throws Exception {
                            // Simulate long-running task
                            for (int i = 1; i <= 5; i++) {
                                Thread.sleep(1000);
                                publish("Processing " + i + "...");
                            }
                            return "Task Completed!";
                        }
                        @Override
                        protected void process(List<String> chunks) {
                            // Update UI on EDT
                            label.setText(chunks.get(chunks.size() - 1));
                        }
                        @Override
                        protected void done() {
                            try {
                                label.setText(get()); // Final result
                            } catch (Exception ex) {
                                label.setText("Error: " + ex.getMessage());
                            }
                        }
                    };
                    worker.execute();
                });
                frame.setVisible(true);
            });
        }
    }
  • Key Points:

    • Always create and modify Swing components on the EDT (invokeLater()).
    • Use SwingWorker for background tasks to keep the UI responsive.
    • Avoid long-running tasks on the EDT to prevent freezing.
    • Handle exceptions (e.g., InterruptedException, ExecutionException) in SwingWorker.

Connecting to Previous Topics

  • Packages:

    • Swing classes are in javax.swing and java.awt.
    • Custom Swing applications should be organized in packages (e.g., com.example.myapp.gui).
    • Ensure CLASSPATH includes Swing libraries (standard in Java).
  • Interfaces:

    • Event listeners (e.g., ActionListener, MouseListener) are interfaces.
    • Example: public class MyListener implements ActionListener.
    • Swing components implement interfaces like Serializable and Accessible.
  • Exception Handling:

    • Swing operations may throw unchecked exceptions (e.g., NullPointerException, IllegalArgumentException).
    • SwingWorker tasks may throw InterruptedException or ExecutionException.
    • Example:
      try {
          Thread.sleep(1000); // In SwingWorker
      } catch (InterruptedException e) {
          System.err.println("Task interrupted");
      }
  • Multithreading:

    • Swing relies on the EDT, a single-threaded model.
    • Use SwingWorker or invokeLater() for thread-safe GUI updates.
    • volatile or synchronized methods may be used in Swing applications for shared data.
  • I/O:

    • Swing applications often read/write files (e.g., saving user preferences).
    • Example: Use JFileChooser for file selection.
      JFileChooser chooser = new JFileChooser();
      if (chooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
          File file = chooser.getSelectedFile();
          // Read file
      }
  • Strings and Characters:

    • Swing components use String for text (e.g., JLabel.setText("Hello")).
    • Use StringBuilder for dynamic text updates in SwingWorker.
    • Example:
      StringBuilder sb = new StringBuilder();
      sb.append("Result: ").append(count);
      label.setText(sb.toString());

Practice Program:

package com.example.myapp;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SwingApp {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Swing App");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 300);
            // Panel with BorderLayout
            JPanel panel = new JPanel(new BorderLayout());
            // Top: Input
            JPanel topPanel = new JPanel(new FlowLayout());
            JTextField textField = new JTextField(10);
            topPanel.add(new JLabel("Enter text:"));
            topPanel.add(textField);
            panel.add(topPanel, BorderLayout.NORTH);
            // Center: Output
            JLabel outputLabel = new JLabel("Output: ");
            panel.add(outputLabel, BorderLayout.CENTER);
            // Bottom: Button
            JButton button = new JButton("Submit");
            button.addActionListener(e -> {
                SwingWorker<Void, String> worker = new SwingWorker<>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        publish("Processing: " + textField.getText());
                        Thread.sleep(1000);
                        return null;
                    }
                    @Override
                    protected void process(java.util.List<String> chunks) {
                        outputLabel.setText(chunks.get(chunks.size() - 1));
                    }
                };
                worker.execute();
            });
            panel.add(button, BorderLayout.SOUTH);
            frame.add(panel);
            try {
                UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
            } catch (Exception ex) {
                System.err.println("Look and feel error: " + ex.getMessage());
            }
            frame.setVisible(true);
        });
    }
}

Compile and Run:

javac com/example/myapp/SwingApp.java
java -cp . com.example.myapp.SwingApp