Session 22: Graphical user interfaces

Components and containers (Section 12.2)
    The JFrame class
    Containers
    Components
Interfaces (Section 12.3, part 2)
    Defining an interface
    Implementing an interface
    Interface variables
Event listeners (Section 12.3)
    Creating a listener
    Registering a listener

Components and containers

Textbook: Section 12.2

Many common programs involve graphical user interfaces. We're going to look at Java's libraries for building GUIs, called Swing. (There's an older library Java used for this, called AWT. This is what the textbook covers. We're going to look at Swing in this class, though.)

The JFrame class

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

import java.awt.*;       // we'll include these three import lines in
import java.awt.event.*; // all our programs using Swing.
import javax.swing.*;

public class EmptyWindow extends JFrame {
    public EmptyWindow() {
        pack();
    }

    public static void main(String[] args) {
        EmptyWindow window = new EmptyWindow();
        window.show();
    }
}
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()
(Constructor method) Creates a JFrame to represent a window.

void pack()
Resizes the window to fit what it contains.

void show()
Displays the window on the screen.

Container getContentPane()
Returns an object into which you can place the graphical user interfaces on the screen.

Containers

A Container is an object used for holding components. (Examples of components include buttons and text fields.) For the moment, we're just going to pay attention to one of its methods.

void add(Component what, Object info)
Inserts what into the container, using info as information about where the object should be placed.

The info parameter to add(), saying where to place the object, can have one of several possibilities.
BorderLayout.NORTHBorderLayout.EAST
BorderLayout.SOUTHBorderLayout.WEST
BorderLayout.CENTER
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, but play along for the moment.)

Components

Finally, there are the components. The first one of these we'll see is the JButton, which represents a button on the screen. For the moment, we'll just worry about the constructor method.

JButton(String label)
Creates a button holding the string label.

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

JTextField()
Creates a text field.

String getText()
Returns the current text inside the field.

void setText(String text)
Changes the current text of the field to text.

Here's a program that creates a window containing two buttons and a text 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.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonExample extends JFrame {
    JButton incr_button;
    JButton quit_button;
    JTextField number_field;

    public ButtonExample() {
        incr_button = new JButton("Increment");
        quit_button = new JButton("Quit");
        number_field = new JTextField();

        Container contents = getContentPane();
        contents.add(incr_button, BorderLayout.NORTH);
        contents.add(number_field, BorderLayout.CENTER);
        contents.add(quit_button, BorderLayout.SOUTH);
        pack();
    }

    public static void main(String[] args) {
        (new ButtonExample()).show();
    }
}
When executed, this makes a window containing a text field sandwiched by a button labeled ``Increment'' at the top and a button labeled ``Quit'' below.

Interfaces

Textbook: Section 12.3, part 2

Before we continue, 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 introducing with graphical user interfaces.

Defining an interface

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.

For example, we might have defined the following interface in our Breakout game.

public interface MovingObject {
    public void step(DrawableFrame f);
    public void draw(Graphics g);
}
This was an interface that both the Ball and the Paddle classes implemented.

Implementing an interface

When you define a class that implements the interface, you need to say so up front with an implements clause.

public class Ball implements MovingObject { //...
public class Paddle extends Block implements MovingObject { //...
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.

(An astute observer will notice that in Breakout, Part III, we redefined Ball's step method to return a boolean. That class didn't really implement the MovingObject interface we explained, and the compiler would refuse to let us get away with it.)

Interface variables

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

DrawableFrame frame = new BreakoutFrame();
MovingObject mover = new Ball();
mover.step(frame);
mover.draw(frame.getGraphics());
Naturally, you can never create an instance of an interface.

So, interfaces in a certain sense allow you to have a class have two ``superclasses.'' Only one of these is a true Java class, but the behavior as far as converting between types makes both the implemented interface and the real parent class behave like superclasses.

Event listeners

Textbook: Section 12.3

Creating a listener

So how can we have our button do something? We're going to have to use an interface - namely, the ActionListener class 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 paremeter 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.

had an action

In order to define what to do for the button, we're going to need an object that implement this interface. Inside that object's actionPerformed() method, we'll have some code that say what to do.

Registering a listener

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.*;
import java.awt.event.*;
import javax.swing.*;

public class ListeningExample extends JFrame implements ActionListener {
    JButton incr_button;
    JButton quit_button;
    JTextField number_field;

    public ListeningExample() {
        incr_button = new JButton("Increment");
        quit_button = new JButton("Quit");
        number_field = new JTextField();

        incr_button.addActionListener(this);
        quit_button.addActionListener(this);

        Container contents = getContentPane();
        contents.add(incr_button, BorderLayout.NORTH);
        contents.add(number_field, BorderLayout.CENTER);
        contents.add(quit_button, BorderLayout.SOUTH);
        pack();
    }

    public void actionPerformed(ActionEvent evt) {
        if(evt.getSource() == incr_button) {
            int i = Integer.parseInt(number_field.getText());
            number_field.setText("" + (i + 1));
        } else if(evt.getSource() == quit_button) {
            System.exit(0); // The System.exit class method halts entire program
        }
    }

    public static void main(String[] args) {
        (new ListeningExample()).show();
    }
}

The important changes here are that we've added two lines in the ListeningExample constructor method registering 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. What it does is first to see which component triggered the action - that is, whether the Increment or Quit button was clicked. It performs an action based on this.