Java swing package

Also includes a brief overview on an unrelated topic: file I/O in Java.

Overview

The Swing package is part of the JavaTM Foundation Classes (JFC) in the Java platform. The JFC encompasses a group of features to help in building GUIs (graphical user interfaces). Swing provides components such as panels, buttons, selection boxes, etc.

Previous versions of Java (jdk 1.0, 1.1) have used AWT package for these purposes, which provides similar features. Although the Java 2 Platform still supports the AWT components, Swing components are easier to use and provide far more functionality. You can identify Swing components because their names start with J. The AWT button class, for example, is named Button, whereas the Swing button class is named JButton. In addition, the AWT components are in the java.awt package, whereas the Swing components are in the javax.swing package.

How to use swing with applications and applets

As you know, there are two kinds of Java programs: applications and applets. The main difference between them is that an application is run directly by Java runtime system (in LINUX you use command java followed by the class name of the class that has the method main), but an applet must be invoked from an html file. To run an applet in LINUX, you need an html file of the form

<html>
<body>
<applet code = "JAppletExample.class" WIDTH = 400 HEIGHT = 400>
</applet>
</body>
</html>
To start an applet, type appletviewer JAppletExample.html (or another file name) at the LINUX promp. We will be mostly working with applications, but it might be helpful for you to know how to run applets so that you can test examples of applets.

From the programming point of view, an application defines a public class in which there is a method named main. It may also define other classes, both public and private, but no other class may have a method main. This is the method that starts executing when the program starts.

An applet must extend the class Applet or a descendent of Applet (s.a. JApplet introduced below), which means that it cannot extend any other class, since a class has only one parent (introduction of the Runnable interface for threads was largely due to this restriction: to make a class to be both an applet and runnable, we can extend the class Applet and implement Runnable. However, it is not possible for a class to be both a thread and an applet, since it is impossible to extend both Applet and Thread at the same time).

Recall that an applet has methods init(), start(), run(), and stop(), among others, but no main() method.

In the examples below we will see how to use Swing with both applications and applets.

Example of a Swing applications

Below is an example of a very simple program:

              import javax.swing.*;        

              public class HelloWorldSwing {
                  public static void main(String[] args) {
                      JFrame frame = new JFrame("HelloWorldSwing");
                      final JLabel label = new JLabel("Hello World");
                      frame.getContentPane().add(label);

                      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                      frame.pack();
                      frame.setVisible(true);
                  }
              }
The first line imports the main Swing package:

              import javax.swing.*;
This is the only package that HelloWorldSwing needs. However, most Swing programs also need to import two AWT packages:

              import java.awt.*;
              import java.awt.event.*;
These packages are required because Swing components use the AWT infrastructure, including the AWT event model. The event model governs how a component reacts to events, such as button clicks and mouse motion.
Top-level containers
Every program with a Swing GUI must contain at least one top-level Swing container. A top-level Swing container provides the support that Swing components need to perform their painting and event handling. There are three top-level Swing containers: JFrame, JDialog, and (for applets) JApplet.

Each JFrame object implements a single main window, and each JDialog implements a secondary window (a window that's dependent on another window). Each JApplet object implements an applet's display area which is created when the html file for the applet is started in appletviewer or in a browser window.

Since our example is an application (it has main method), we use JFrame as the top-level container.

Working with JFrame Here is the code that sets up and shows the frame:

              JFrame frame = new JFrame("HelloWorldSwing");
              ...
              frame.pack();
              frame.setVisible(true);
To close the window when the close button is clicked, we include this code in our HelloWorldSwing program:

              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Adding components to containers
HelloWorldSwing has one component, a label that reads "Hello World." These two lines of code construct and then add the component to the frame:

              final JLabel label = new JLabel("Hello World");
              frame.getContentPane().add(label);
These two steps are a pattern that you will use to add any component to a container:
  1. Create and initialize a component.
  2. Add it to the container by invoking the add() method on the container object.

Swing components

The most commonly used components are those used for control (s.a. JButton, JRadioButton, JCheckBox), menus (s.a. JMenuBar, JPopupMenu, menus also may be constructed out of buttons), text areas (JTextArea, JTextField, JPasswordField, etc.), tables, and many others.

Some components are themselves containers, so you can add other components to them. Those include JPanel (a general-purpose container, most commonly used to implement nesting of components), JScrollPane, which rovides a scrollable view of components, JSplitPane, which displays two components, either side by side or one on top of the other, with a divider that one can drag to specify how much of the split pane's area goes to each component.

You can learn (much!) more about Swing components at A visual index to the Swing Components at java.sun.com, see also Overview of hierarchy of components for a brief overview of Swing components. And, of course, check out the APIs for the details of the methods!

Positioning components in a container: layouts

Notice that when a component is added in the container, nothing in the add() method specifies where the component will be positioned. For instance, if you add two buttons to a frame, you don't specify if they should be located side by side or one above the other. This is because Java separates the structure of a container (i.e. which components it contains) from its appearance.

The appearance is controlled by specifying a layout of a container, i.e. the way components added to the container will be visually organized. Each component has a layout manager. If you don't specify a layout manager explicitly, then the default one for this container will be used. There are five layout managers: BorderLayout, BoxLayout, FlowLayout, GridBagLayout, and GridLayout. You can see examples of using various layouts, as well as further links, at Layout Management tutorial at java.sun.com.

You might find it helpful to use nested containers (s.a. JPanel) to control the appearance of your GUI. For instance, if you want to position three buttons under a text area, you can add the buttons to a JPanel using grid layout with one row and three columns, and then add the text area and the JPanel to your frame using grid layout with 2 rows, 1 column.

"Look and feel"

Since Java programs run on different platforms, one might want to make the GUI look similar to a traditional GUI of the system (in the way the buttons look, alerts appear, and so on). Sun tutorial on How to Set the Look and Feel provides more information.

Handling events

To be able to do something useful with your GUI you need to be able to handle user's input, s.a. buttons clicked, text entered, keys pressed, and so on. You do this by associating event listeners with components that allow input.

Every time the user types a character or pushes a mouse button, an event occurs. Any object can be notified of the event. All the object has to do is implement the appropriate interface and be registered as an event listener on the appropriate event source.

Every event handler requires three pieces of code:

  1. In the declaration for the event handler class, one line of code specifies that the class either implements a listener interface or extends a class that implements a listener interface. For example:
    
                        public class MyClass implements ActionListener { 
    
  2. Another line of code registers an instance of the event handler class as a listener on one or more components. For example:
    
                        someComponent.addActionListener(instanceOfMyClass);
    
  3. In the event handler class, a few lines of code implement the methods in the listener interface. For example:
    
                        public void actionPerformed(ActionEvent e) { 
                            ...//code that reacts to the action... 
                        } 
    
Swing components can generate many kinds of events. The following table lists a few examples.
Examples of Events and Their Associated Event Listeners
Act that Results in the Event Listener Type
User clicks a button, presses Return while typing in a text field, or chooses a menu item ActionListener
User closes a frame (main window) WindowListener
User presses a mouse button while the cursor is over a component MouseListener
User moves the mouse over a component MouseMotionListener
Component becomes visible ComponentListener
Component gets the keyboard focus FocusListener
Table or list selection changes ListSelectionListener

Event-handling code executes in a single thread, the event-dispatching thread. This ensures that each event handler finishes execution before the next one executes. Painting code also executes in the event-dispatching thread. Therefore, while the method that handles the action is executing, the program's GUI is frozen--it won't repaint or respond to mouse clicks.

Putting it all together: another Swing program

A simple program below that counts the number of clicks on a button illustrates many important features of Swing programs: nesting of containers, adding components, handling events.

//v 1.3
import javax.swing.*;          
import java.awt.*;
import java.awt.event.*;

public class SwingApplication {
    private static String labelPrefix = "Number of button clicks: ";
    private int numClicks = 0;

    public Component createComponents() {
        final JLabel label = new JLabel(labelPrefix + "0    ");

        JButton button = new JButton("I'm a Swing button!");
        button.setMnemonic(KeyEvent.VK_I);
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                numClicks++;
                label.setText(labelPrefix + numClicks);
            }
        });
        label.setLabelFor(button);

        /*
         * An easy way to put space between a top-level container
         * and its contents is to put the contents in a JPanel
         * that has an "empty" border.
         */
        JPanel pane = new JPanel();
        pane.setBorder(BorderFactory.createEmptyBorder(
                                        30, //top
                                        30, //left
                                        10, //bottom
                                        30) //right
                                        );
        pane.setLayout(new GridLayout(0, 1));
        pane.add(button);
        pane.add(label);

        return pane;
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(
                UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (Exception e) {}

        //Create the top-level container and add contents to it.
        JFrame frame = new JFrame("SwingApplication");
        SwingApplication app = new SwingApplication();
        Component contents = app.createComponents();
        frame.getContentPane().add(contents, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}

A new action listener class is created right as the action listener is added to the button. Alternatively one could write the action class separately:


    private class MyActionListener implements ActionListener{
	public void actionPerformed(ActionEvent e) {
	    numClicks++;
	    label.setText(labelPrefix + numClicks);
	}

    }
Here e is the event. A new action listener MyActionListener is added to the button:

    button.addActionListener(new MyActionListener());
Note that the event handling code in the method void above refers to the variable label, which should be made global.

Event listeners react only to events in the components for which they are registered (by an add method). If an event listener is registered for several components, you can use getSource() method of an event object to check in which component the event has occured. Check out the examples in Event handling tutorial at java.sun.com, they are simple and very helpful.

Unrelated topic: reading and writing files

There are two ways of reading and writing files in Java: as character streams and as byte streams. Since byte streams are more general (they allow reading and writing not only of text-based files, but also of graphics and other kinds of files) and character streams are based on byte streams anyway, we'll study byte streams here. Below is an example of a program which copies a file infile.txt into a file outfile.txt:

import java.io.*;

public class CopyBytes {
    public static void main(String[] args) throws IOException {
        File inputFile = new File("infile.txt");
        File outputFile = new File("outfile.txt");

        FileInputStream in = new FileInputStream(inputFile);
        FileOutputStream out = new FileOutputStream(outputFile);
        int c;

        while ((c = in.read()) != -1)
           out.write(c);

        in.close();
        out.close();
    }
}
Important things to note about this program:
  1. You need to import java.io package
  2. You need to create file streams for the files: the input stream for the input file, and the output stream for the output file
  3. You use an integer variable to store individualbytes of the file. The character would have been used for character streams.
  4. The reading and writing loop goes until c becomes -1, which signals the end of the file
  5. Both the input and output files must be closed in the end. The system would usually automatically close all files when the program terminates, but you don't want to rely on it. Besides, you might want to use a file more than once in the same program.
  6. The method throws IOException. You might want to catch the exception in your program to display an error message and continue, but if you don't catch the exception, then you need to specify throws IOException in the headers of all methods that the exception goes through.
  7. If the input file does not exist, the FileNotFoundException occurs.
  8. If the output file does not exist, it will be created, if it exists, then it will be overwritten.

Some material on this page has been adopted from a subset of online sources listed here
This page has been created and is maintained by Elena Machkasova
Comments and suggestions are welcome at emachkas@wellesley.edu

Spring Semester 2002