1.124 Lecture 13 10/24/2000

Graphical Programs

Contents

1. Introduction

Graphical programs require a very different programming model to the non-graphical programs we have encountered in the past.  A non-graphical program typically runs straight through from beginning to end.  By contrast, a graphical program should be capable of running indefinitely, accepting input through the graphical user interface (GUI) and responding accordingly.  This kind of programming is known as event-driven programming, because the program's sequence of operation is determined by events generated by the GUI components.  The program responds to events by invoking functions known as event handlers.  For example, pushing the Print button may generate a "button-pushed" event, which results in a call to an event handler named print().

In general, a graphical program consists of the following key elements:

The following pseudo-code illustrates how the event loop might work

    while (true) {                                        // The event loop.
        // Get the next event from the event queue.
        Event e = get_next_event();

        // Process the events by calling appropriate event handlers.
        if (e.eventType == QUIT) {
            exit();                                           // Terminate the program.
        }
        else if (e.eventType == BUTTON_PUSHED) {
            if (e.eventSource == PRINT_BUTTON)
                print(e);                                   // Print out the current page.
            else {
                ...
            }
        }
        else {
            ...
        }
    }

In C++, the programmer must often explicitly write an event loop similar to the one shown above.  This can involve a lot of work, so Java attempts to shield the programmer from the actual event loop, while still providing a flexible way to specify how events are processed.
 

2. The Java Event Model (JDK 1.1 and above)

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/uiswing/overview/event.html)

The Java event model is based on the notion of event sources and event listeners.

The event listener registration and notification process takes place according to event type.  An object wishing to listen to events of a particular type must implement the corresponding event listener interface.  The interface simply specifies a standard set of event handling functions that the listener object must provide.

Here is a list of events, and their corresponding event types and event listener interfaces.
Event Event Type Event Listener Interface
Button click, menu selection, text field entry ActionEvent ActionListener
Resizing, moving, showing or hiding a component ComponentEvent ComponentListener
Mouse press, mouse release, mouse click, mouse enter, mouse exit MouseEvent MouseListener
Mouse move, mouse drag MouseEvent MouseMotionListener
Key press, key release KeyEvent KeyListener
Gain keyboard focus, lose keyboard focus FocusEvent FocusListener
Window closing, window iconified, window deiconified WindowEvent WindowListener
Scrolling AdjustmentEvent AdjustmentListener
Item selection e.g. checkbox, list item ItemEvent ItemListener
Return key pressed TextEvent TextListener
Adding/removing a component to/from a container ContainerEvent ContainerListener

The general approach to implementing an event listener is the same in every case.

The following example shows how to create a frame.  When the frame is closed, we want to make sure that the program terminates, since this does not happen automatically.  We can use a WindowListener to do this.

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

public class Main {
    public static void main(String[] args) {
        // Create a window.  Then set its size and make it visible.
        JFrame frame = new JFrame("Main window");
        frame.setSize(400,400);
        frame.setVisible(true);

        // Make the program terminate when the frame is closed.  We do this by registering a window listener
        // to receive WindowEvents from the frame.  The window listener will provide an event handler called
        // windowClosing, which will be called when the frame is closed.
        WindowListener listener = new MyWindowListener();                 // A class that we write.
        frame.addWindowListener(listener);
    }
}

// Here is our window listener.  We are only interested in windowClosing, however, we must provide
// implementations for all of the methods in the WindowListener interface.
class MyWindowListener implements WindowListener {
    public void windowClosing(WindowEvent e) {
        System.out.println("Terminating the program now.");
        System.exit(0);
    }
    public void windowClosed(WindowEvent e) {}
    public void windowOpened(WindowEvent e) {}
    public void windowActivated(WindowEvent e) {}
    public void windowDeactivated(WindowEvent e) {}
    public void windowIconified(WindowEvent e) {}
    public void windowDeiconified(WindowEvent e) {}
}
 

Unfortunately, this example involves quite a lot of code.  There are a couple of ways to simplify the program
 

Anonymous classes

An anonymous class is a class that has no name.  It is declared an instantiated within a single expression.  Here is how we could use an anonymous class to simplify the closable frame example:

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

public class Main {
    public static void main(String[] args) {
        // Create a window.  Then set its size and make it visible.
        JFrame frame = new JFrame("Main window");
        frame.setSize(400,400);
        frame.setVisible(true);

        // Make the frame closable.  Here we have used an anonymous class that implements the
        // WindowListener interface.
        frame.addWindowListener(new WindowListener() {
            public void windowClosing(WindowEvent e) {
                System.out.println("Terminating the program now.");
                System.exit(0);
            }
            public void windowClosed(WindowEvent e) {}
            public void windowOpened(WindowEvent e) {}
            public void windowActivated(WindowEvent e) {}
            public void windowDeactivated(WindowEvent e) {}
            public void windowIconified(WindowEvent e) {}
            public void windowDeiconified(WindowEvent e) {}
        });
    }
}
 

Event adapters

An event adapter is just a class that implements an event listener interface, with empty definitions for all of the functions.  The idea is that if we subclass the event adapter, we will only have to override the functions that we are interested in.  The closable frame example can thus be shortened to:

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

public class Main {
    public static void main(String[] args) {
        // Create a window.  Then set its size and make it visible.
        JFrame frame = new JFrame("Main window");
        frame.setSize(400,400);
        frame.setVisible(true);

        // Make the frame closable.  Here we have used an anonymous class that extends WindowAdapter.
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {    // This overrides the empty base class method.
                System.out.println("Terminating the program now.");
                System.exit(0);
            }
        });
    }
}
 

3. Laying Out User Interface Components

Containers

(Ref. Java Tutorial http://java.sun.com/docs/books/tutorial/uiswing/overview/hierarchy.html)

A Container is a GUI component that can hold other GUI components.  Three commonly used container classes are

A component object, myComponent, can be added to a container object, myContainer, using a statement of the form

    myContainer.getContentPane().add(myComponent);

The following example illustrates how to add a JButton instance to an instance of JFrame.

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

public class Main {
    public static void main(String[] args) {
        // Create a window.
        JFrame frame = new JFrame("Main window");
        frame.setSize(400,400);

        // Create a button and add it to the frame.
        JButton button = new JButton("Click me");
        frame.getContentPane().add(button);

        // Add an event handler for button clicks.
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {      // Only one method to implement.
                System.out.println(e.getActionCommand());     // Prints out "Click me".
            }
        });

        // Make the frame closable.
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        // Make the frame visible after adding the button.
        frame.setVisible(true);
    }
}
 

Layout Managers

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/uiswing/layout/using.html)

Our previous example has only one interesting GUI component: a JButton.  What if we wanted to add a second JButton and perhaps a JTextArea, so that we can display the message through the GUI?  We can control the layout of these components within the container by using a layout manager.  Java comes with six layout managers (five in java.awt and one in javax.swing)

It is also possible to set a null layout manager and instead position components by specifying their absolute coordinates using the method

    public void setLocation(int x, int y)

Suppose we wish to position our two JButtons side by side, with the JTextArea positioned below them.  We start by embedding the JButtons within a JPanel, using FlowLayout as the layout manager for the JPanel.  The JTextArea is best placed within a JScrollPane, since this will permit scrolling when the amount of text exceeds the preferred size of the scroll pane.  We can now attach the JPanel and the JScrollPane to the North and South borders of the JFrame, by using BorderLayout as the layout manager for the JFrame.  These containment relationships are illustrated below:
 
JFrame
JPanel (attached to the North border of JFrame)

 
JButton JButton
(laid out using FlowLayout)
 
JScrollPane (attached to the South border of JFrame)

JTextArea
 

Here is the implementation:

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

public class Main {
    public static void main(String[] args) {
        // Create a window and set its layout manager to be BorderLayout.
        // (This happens to be the default layout manager for a JFrame.)
        JFrame frame = new JFrame("Main window");
        frame.setSize(400,400);
        Container cf = frame.getContentPane();
        cf.setLayout(new BorderLayout());

        // Create a panel and set its layout manager to be FlowLayout.
        // (This happens to be the default layout manager for a JPanel.)
        JPanel panel = new JPanel();
        panel.setLayout(new FlowLayout());     // No content pane for JPanel.

        // Create two buttons and add them to the panel.
        JButton button1 = new JButton("Left");
        JButton button2 = new JButton("Right");
        panel.add(button1);
        panel.add(button2);

        // Create a text area for displaying messages.  We embed the text
        // area in a scroll pane so that it doesn't grow unboundedly.
        JTextArea textArea = new JTextArea();
        JScrollPane scrollPane = new JScrollPane(textArea);
        scrollPane.setPreferredSize(new Dimension(400, 100));
        textArea.setEditable(false);

        // Position the panel and the text area within the frame.
        cf.add(panel, "North");
        cf.add(scrollPane, "South");

        // Add event handlers for button clicks.
        class MyListener implements ActionListener {     // A local class.
            private JTextArea mTextArea;
            public void setTextArea(JTextArea t) {
                mTextArea = t;
            }
            public void actionPerformed(ActionEvent e) {
                mTextArea.append(e.getActionCommand()+"\n");
            }
        }
        MyListener listener = new MyListener();
        listener.setTextArea(textArea);      // Cannot do this with an anonymous class.
        button1.addActionListener(listener);
        button2.addActionListener(listener);

        // Make the frame closable.
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        // Make the frame visible after adding the components to it.
        frame.setVisible(true);
    }
}
 

The following link is a useful reference on using layout managers.

http://java.sun.com/docs/books/tutorial/uiswing/layout/using.html
 

4. Swing Component Overview

(Ref. Java Tutorial: http://java.sun.com/docs/books/tutorial/uiswing/components/components.html, http://java.sun.com/docs/books/tutorial/uiswing/components/example-swing/index.html)

The components that we have seen so far are JFrame, JPanel, JButton, JTextArea and JScrollPane.  The links below provide a good overview of the Swing components and how to use them.