CSci 151: Foundations of computer science II
Home Syllabus Assignments Tests

Swing GUIs I

Many common programs involve graphical user interfaces. We're going to look at Java's libraries for building GUIs, called Swing.

§1. The simplest window

The first class we'll look at is JFrame, which represents a window. Consider the following simple program.

import javax.swing.JFrame;

public class EmptyWindow {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Empty Window");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}

This particular program isn't very useful: It just creates a frame and displays it, effectively showing an empty window.

A JFrame has just a few methods worth talking about for now.

JFrame(String title)

(Constructor) Creates a JFrame to represent a window, with title in its title bar.

void setDefaultCloseOperation(int value)

Configures how this window behaves when the user clicks on the close-window button in the title bar. For simple programs you will want this to be JFrame.EXIT_ON_CLOSE. The default behavior is JFrame.HIDE_ON_CLOSE; the window disappears from the screen but the base program may continue to execute.

void pack()

Resizes this window to fit what it contains.

void setVisible(boolean value)

Configures whether this window is visible on the screen.

Container getContentPane()

Returns an object into which you can place the graphical user interfaces on the screen.

§2. Inserting components

A Container is an object used for holding components such as buttons and text fields. For the moment, we're going to pay attention to just one of the Container class's methods.

void add(Component what, Object info)

Inserts what into this container, using info as information about where the object should be placed.

Some possibilities for the info parameter to add include:

BorderLayout.NORTH
BorderLayout.WEST     BorderLayout.CENTER     BorderLayout.EAST
BorderLayout.SOUTH

These say where to place the component into the window. You'll notice that this is pretty restricted. There are other ways to lay out components in the window, and we'll get to them eventually.

More significant is the add method's Component parameter. Though Component is a class, you'll never write new Component() to create an instance. Instead, you'll create instances of subclasses of Component, such as JButton for buttons or JTextField for text fields.

For now, the only JButton documentation we'll need in for its constructor.

JButton(String label)

(Constructor) Creates a button holding the string label.

Another type of component is the text field, where the user can type something.

JTextField(int columns)

(Constructor) Creates a text field, sized to contain columns characters.

String getText()

Returns the current text inside this field.

void setText(String text)

Changes the current text of this field to text.

Using JButton and JTextField, we can build a program that creates a window containing a text field sandwiched by two buttons labeled Increment and Quit. field. We're going to work on making the buttons do something soon. For the moment, all the program does is display the components.

import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class ButtonExample {
    public static void main(String[] args) {
        JButton incrButton = new JButton("Increment");
        JButton quitButton = new JButton("Quit");
        JTextField numberField = new JTextField(8);

        JFrame frame = new JFrame("Button Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container contents = frame.getContentPane();
        contents.add(incrButton, BorderLayout.NORTH);
        contents.add(numberField, BorderLayout.CENTER);
        contents.add(quitButton, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

§3. Interfaces

Now we can work on allowing the user to interact with the window. In order to do this, we need to discuss interfaces. An interface is basically just a set of methods. Any class that hopes to implement the interface must implement all of the methods in the interface's set.

Despite the name, interfaces are not specific to graphical user interfaces! They're a general Java concept, that we just happen to be discussing in the context of graphical user interfaces.

Defining an interface looks a lot like defining a class, except that we can only list instance methods, and none of the instance methods have any bodies. In place of each instance method's body, we just place a semicolon.

For example, we might have defined the following interface for classes that have a position.

public interface Locatable {
    public double getX();
    public double getY();
}

With the interface defined, we will now need classes that claim to implement the interface. For this, we need to say so up front using an implements clause.

public class Ball implements Locatable { //...
public class Paddle extends Block implements Locatable { //...
Any class that has such an implements clause has to define all of the methods defined in the interface. Otherwise, there's a compiler error. Thus, both Ball and Paddle must have getX and getY methods; since the interface says these methods take no parameters and return a double, they need to be defined this way in Ball and Paddle too.

What makes interfaces useful is that they are types, too. The following would be completely legitimate.

Locatable loc = new Ball();
System.out.println(loc.getX() + ", " + loc.getY());
loc = new Paddle();
System.out.println(loc.getX() + ", " + loc.getY());

You cannot create an instance of an interface: new Locatable() would be illegal.

In some sense, with interfaces a class have two superclasses. Only one of these can be a true Java class, but the type conversion behavior makes it feel as if both the actual parent class and the implemented interface are superclasses.

§4. Event listeners

So how can we have our button do something? We're going to have to use an interface - namely, the ActionListener interface in the java.awt.event package, which includes the following method.

public void actionPerformed(ActionEvent evt)

Called when an action is performed on a GUI component. In the case of a button, the action occurs when the user clicks on it.

The ActionEvent parameter specifies information about the event that has occurred. For the moment, we're just going to worry about its getSource method, which returns the Component on which the action event was performed.

In order to define what to do for the button, we're going to need a class that implements this interface. Inside that class's actionPerformed method, we'll have some code that say what to do.

The other step in setting up the program to handle events is to register the ActionListener object with the component. In our case, we'll want the addActionListener method in the JButton class. It takes takes an ActionListener as an argument and sets up the button so that, when clicked, it calls the actionPerformed method in that ActionListener.

The changes from before are drawn in boldface here.

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class ListeningExample implements ActionListener {
    private JButton incr;
    private JButton quit;
    private JTextField field;

    private ListeningExample(JButton i, JButton q, JTextField f) {
        incr = i; quit = q; field = f;
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == incr) {
            int i = Integer.parseInt(field.getText());
            field.setText("" + (i + 1));
        } else if(e.getSource() == quit) {
            System.exit(0);
        }
    }

    public static void main(String[] args) {
        JButton incrButton = new JButton("Increment");
        JButton quitButton = new JButton("Quit");
        JTextField numberField = new JTextField(8);

        ListeningExample listener = new ListeningExample(incrButton,
            quitButton, numberField);
        incrButton.addActionListener(listener);
        quitButton.addActionListener(listener);

        JFrame frame = new JFrame("Listening Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container contents = frame.getContentPane();
        contents.add(incrButton, BorderLayout.NORTH);
        contents.add(numberField, BorderLayout.CENTER);
        contents.add(quitButton, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

The important changes here are that we've added two lines in the main method using the addActionListener method to register the ListeningExample object to listen to actions being performed on its two buttons. That is, when somebody clicks on either button, it will call the actionPerformed method on the constructed ListeningExample object.

The other important change is the addition of the actionPerformed method. In our case, this method first sees of the components triggered the action — that is, whether the Increment or Quit button was clicked. It performs an action based on this.

§5. Composing GUIs

We don't yet have the ability to build complex windows: We can create components, and we can place them on the north, south, east, or west edge of the window; or we can place it in the window's center. That limits us to five components, and they're not necessarily placed how we want them.

§5.1. The JPanel class

Building more complex interfaces requires using the JPanel class. A JPanel object is an empty graphical component, but it is a subclass of Container, so you can add other components into it.

There are four JPanel methods that are particularly important.

JPanel()

(Constructor) Creates an empty JPanel.

void add(Component what)

Inserts what into this panel, using the default placement.

void add(Component what, Object info)

Inserts what into this panel, using info as information about where the object should be placed.

void setLayout(LayoutManager manager)

Configures this container to use the indicated technique for laying out its components. We'll see how this works soon.

§5.2. Layouts

There are several categories of classes involved in creating GUIs using Swing, and we've covered all but the last.

This last category consists of classes implementing Java's LayoutManager interface. The layout classes tell a container class how it should arrange the components it contains. We can use the setLayout method to configure a container, such as Container or JPanel, to use a different approach for arranging its components.

BorderLayout

We've actually already seen the BorderLayout class in passing: It's the default layout for a JFrame's container. Creating a BorderLayout object is simple:

BorderLayout()

(Constructor) Creates a BorderLayout object.

When you add something to a container that's using the BorderLayout, you will generally use the add method that takes an info parameter, and you will pass something like BorderLayout.NORTH saying on which border to place the component (or BorderLayout.CENTER to place the component in the middle).

FlowLayout

The FlowLayout class is even simpler: It places the components in left-to-right order. Each component gets sized to its preferred size. When the components fill the row, they start being placed in the next row.

FlowLayout()

(Constructor) Creates a FlowLayout object.

With a container using FlowLayout, you'll generally use the add that doesn't take an info parameter.

FlowLayout is the default layout for a JPanel.

GridLayout

In the GridLayout class, components are placed left-right, top-down in a strict grid. Each component is resized to fill its grid space.

GridLayout(int rows, int columns)

(Constructor) Creates a GridLayout object, represent the layout strategy with rows rows and columns columns.

§5.3. Case study: Temperature conversion

As an example illustrating the use of JPanel, let's work on a program that creates a window as at right. In this program, the user types a temperature into either blank and presses Enter; the converted temperature then appears in the other blank.

To accomplish this, the program creates a JFrame whose content pane contains two JPanel objects, one on its north edge, one on its south edge. Each JPanel uses its default FlowLayout manager and contains a JLabel and a JTextField.

 1 import java.awt.BorderLayout;
 2 import java.awt.Container;
 3 import java.awt.event.ActionEvent;
 4 import java.awt.event.ActionListener;
 5 import javax.swing.JButton;
 6 import javax.swing.JFrame;
 7 import javax.swing.JLabel;
 8 import javax.swing.JPanel;
 9 import javax.swing.JTextField;
10 
11 public class TempConvert implements ActionListener {
12     private JTextField fahr;
13     private JTextField cels;
14 
15     private TempConvert(JTextField fahr, JTextField cels) {
16         this.fahr = fahr; this.cels = cels;
17     }
18 
19     public void actionPerformed(ActionEvent e) {
20         if(e.getSource() == fahr) {
21             double temp = Double.parseDouble(fahr.getText());
22             double val = (temp - 32) / 1.8;
23             cels.setText("" + Math.rint(val * 100) / 100);
24         } else if(e.getSource() == cels) {
25             double temp = Double.parseDouble(cels.getText());
26             double val = 1.8 * temp + 32;
27             fahr.setText("" + Math.rint(val * 100) / 100);
28         }
29     }
30 
31     public static void main(String[] args) {
32         JTextField fahrField = new JTextField(8);
33         JTextField celsField = new JTextField(8);
34 
35         JPanel fahrPanel = new JPanel();
36         fahrPanel.add(new JLabel("Fahrenheit:"));
37         fahrPanel.add(fahrField);
38 
39         JPanel celsPanel = new JPanel();
40         celsPanel.add(new JLabel("Celsius:"));
41         celsPanel.add(celsField);
42 
43         TempConvert listener = new TempConvert(fahrField, celsField);
44         fahrField.addActionListener(listener);
45         celsField.addActionListener(listener);
46 
47         JFrame frame = new JFrame("Temperature Conversion");
48         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
49         Container contents = frame.getContentPane();
50         contents.add(fahrPanel, BorderLayout.NORTH);
51         contents.add(celsPanel, BorderLayout.SOUTH);
52         frame.pack();
53         frame.setVisible(true);
54     }
55 }

Lines 44 and 45: The JTextField action listener

Notice how we're defining an ActionListener for a text field in lines 44 and 45. If you add an ActionListener to a text field, its actionPerformed method occurs whenever the user types Enter while the keyboard is focused on the text field.

Lines 36 and 40: The JLabel class

Another new thing in this program is its use of JLabel, a component that simply displays an uneditable string to appear in the window. We use the constructor that takes a String as its parameter, which is the string displayed in the window.