Skip to main content

Section 4.4 A Graphical User Interface (GUI)

While command-line interfaces are useful, one of the great advantages of the Java language is that its extensive class library makes it relatively easy to develop applications with Graphical User Interfaces (GUIs). Today nearly all personal computing applications are GUI-based. GUI programming requires event-driven programming, which means that GUI programs react to events that are generated mostly by the user's interactions with elements like buttons in the GUI. We will learn how to use Java's event model to handle simple events. This chapter will provide coding for simple GUIs. More advanced GUI components will be covered in Ch. 13.

Subsection 4.4.1 Java's GUI Components

The Java library comes with two separate but interrelated packages of GUI components, the older java.awt package and the newer javax.swing package. For the most part, the Swing classes supersede the AWT classes. For example, the java.awt.Button class is superseded by the javax.swing.JButton class, and the java.awt.TextField class is superseded by the javax.swing.JTextField class. As these examples show, the newer Swing components add an initial 'J' to the names of their corresponding AWT counterparts.

FigureĀ 4.4.1 illustrates how some of the main components appear in a GUI interface. As shown there, a JLabel is simply a string of text displayed on the GUI, used here as a prompt. A JTextField is an input element that can hold a single line of text. In this case, the user has input his name. A JTextArea is an output component that can display multiple lines of text. In this example, it displays a simple greeting. A JButton is a labeled control element, which is an element that allows the user to control the interaction with the program. In this example, the user will be greeted by the name input into the JTextField, whenever the JButton is clicked. As we will learn, clicking on the JButton causes an event to occur, which leads the program to take the action of displaying the greeting. Finally, all of these components are contained in a JFrame, which is a top-level container. A container is a GUI component that can contain other GUI components.

Figure 4.4.1. Various GUI components from the javax.swing package.

The Swing classes are generally considered to be superior to their AWT counterparts. For one thing, Swing components use a sophisticated object-oriented design known as the model-view-controller (MVC) architecture, which gives them much greater functionality than their AWT counterparts. For example, whereas an AWT Button can only have a string as its label, a Swing JButton can use an image as a label. (See ChapterĀ 13 for a detailed discussion of the MVC architecture.)

Second, Swing components are written entirely in Java which makes them more portable and enables them to behave the same way regardless of the operating system on which they are run. Because of their portability, Swing components are considered lightweight. By contrast, AWT classes use routines that are implemented in the underlying operating system and are therefore not easily portable. Hence, they are considered heavyweight components. Whereas a Swing JButton should look and act the same way regardless of platform, an AWT Button would have a different implementation, and hence a different look and feel, on a Macintosh and on a Windows system. In this book, we will use the new Swing classes in our programs.

Subsection 4.4.2 Class Inheritance: Extending a Superclass

As you recall from Chapter 0, class inheritance is the mechanism by which a class of objects can acquire (inherit) the methods and variables of its superclasses. Just as a horse, by membership in the class of horses, inherits those attributes and behaviors of a mammal, and, more generally, those of an animal, a Java subclass inherits the variables and methods of its superclasses. We sometimes lump together an object's attributes and behaviors and refer to them collectively as its functionality. So we say that an object of a subclass inherits the functionality of all of its superclasses.

By the same token, just as a horse and a cow extend their mammalian attributes and behaviors in their own special ways, a Java subclass extends the functionality of its superclasses in its own special way. Thus, a subclass specializes its superclass.

In ChapterĀ 3, we showed how all classes in the Java hierarchy inherit the toString() method from the Object class. The lesson there was that an object in a subclass can either use or override any public method defined in any of its superclasses. In order to implement GUI programs, we need to look at another way to employ inheritance. In particular, we need to learn how to define a new class by extending an existing class.

We noted in ChapterĀ 2 that unless a class is explicitly defined as a subclass of some other class it is considered implicitly to be a direct subclass of Object. Thus, the GreeterApp class that we defined earlier in this chapter is a subclass of Object. We can make the relationship between GreeterApp and Object explicit by using the extends keyword when we define the GreeterApp class:

public class GreeterApp extends Object { ... }

Thus, the extends keyword is used to specify the subclass/superclass relationships that hold in the Java class hierarchy. We sometimes refer to the subclass/superclass relationship as the isa relationship, in the sense that a horse isa mammal, and a mammal isa animal. Thus, the extends keyword is used to define the isa relationship among the objects in the Java class hierarchy.

Figure 4.4.2. Top-level Swing and AWT classes.

A top-level container is a GUI container that cannot be added to another container; it can only have components added to it. FigureĀ 4.4.2 is a class hierarchy that shows the relationships among some of the top-level Swing and AWT classes. For example, the javax.swing.JFrame class, which represents a top-level window, is a subclass of java.awt.Frame, and the javax.swing.JPanel is a subclass of java.awt.Panel. We can see from this figure that a JFrameisaFrame and an FrameisaWindow and a WindowisaContainer. These subclass/superclass relationships are created in their respective class definitions by using the extends keyword as follows:

public class JFrame extends Frame { ... }
public class Frame extends Window { ... }
public class Window extends Container { ... }

As we will see in the next section, extending a class in this way enables us to create a new class by specializing an existing class.

Subsection 4.4.3 Top-level Windows

Referring again to FigureĀ 4.4.2, notice that all of the Swing components are subclasses of the AWT Container class. This means that Swing components are Container s. They inherit the functionality of the Container class. So Swing components can contain other GUI components. That is why a JButton can contain an image.

All GUI programs must be contained inside some kind of top-level container. Swing provides three top-level container classes: JFrame, JApplet and JDialog. The JApplet class is no longer supported by current browsers and was deprecated by Java SE 9 in 2017. For our basic GUI, we will use a JFrame as the top-level window for stand alone applications.

A JFrame encapsulates the basic functionality of a top-level window. It has what is called a content pane, to which other Swing components, such as buttons and text fields, can be added. Also, it comes with enough built-in functionality to respond to certain basic commands, such as when the user adjusts its size or closes it.

Figure 4.4.3. A simple window.

FigureĀ 4.4.3 shows a simple top-level window as it would be displayed on the console. This window has a title ("My GUI"). It is 200 pixels wide, 150 pixels high, and its top-left corner is located at coordinates (100,150) on the console screen. Like in other graphical systems, points on the Java console always given as an ordered pair, (X, Y), with the horizontal coordinate, X, listed first, followed by the vertical coordinate, Y. The horizontal x-axis extends positively from left to right, and the vertical y-axis extends positively from top to bottom.

The class that created and displayed this window is shown in FigureĀ 4.4.4. Note the use of the extends keyword to define SimpleGUI as a subclass of JFrame. As a subclass, SimpleGUI inherits all of the functionality of a JFrame( FigureĀ 4.4.6) . That is, it can contain other GUI components. It knows how to resize and close itself, and so on.

import javax.swing.*;
public class SimpleGUI extends JFrame
{
    public SimpleGUI(String title)
    {   setSize(200,150);
        setLocation(100, 150);
        setTitle(title);
        setVisible(true); // Displays the JFrame
    } // SimpleGUI()
    public static void main(String args[])
    {   new SimpleGUI("My GUI");
    } // main()
  } // SimpleGUI class
Listing 4.4.4. A top-level window with a title.

The reason we want to define a subclass of JFrame, rather than just use a JFrame instance, is because we want eventually to give our subclass additional functionality that is specialized for our application.

Note how SimpleGUI's main() program creates an instance of SimpleGUI by invoking its constructor. There is no need to use a variable here because there are no further references to this object in this class. However, simply constructing a SimpleGUI will not cause it to appear on the Java console. For that to happen, it is necessary to give it a size and to call its setVisible() method. This is done in the constructor method.

Figure 4.4.6. SimpleGUI is a subclass of JFrame.

The constructor method illustrates how to use some of the methods inherited from JFrame. FigureĀ 4.4.6 shows some of the methods that SimpleGUI inherits from JFrame. We use the setSize() and setLocation() methods to set SimpleGUI's size and location. We use the setTitle() method to set its title. And we use the setVisible() method to cause it to appear on the console.

Subsection 4.4.4 GUI Components for Input, Output, and Control

To enable our top-level window to serve as a user interface, it will be necessary to give it some components. FigureĀ 4.4.7 provides an overview of some of the main Swing components. Generally, there are three types of components, which correspond to the three main functions of a user interface: input, output, and control. A JTextField would be an example of an input component. The user can type text into the text field, which can then be transmitted into the program. A JTextArea is an example of an output component. The program can display text in the text area. Control components enable the user to control the actions of the program. A JButton would be an example of a control component. It can be associated with an action that can be initiated whenever the user clicks it. We might also consider a JLabel to be an output component, because we can use it to prompt the user as to what type of actions to take.

Figure 4.4.7. Swing components.

Let's begin by creating a simple user interface, one that enables us to perform basic input, output, and control operations with a minimum of Swing components. This will allow us to demonstrate the basic principles and techniques of user-interface design and will result in a GUI that can be extended for more sophisticated applications. For this example, we will limit our application to that of simply greeting the user, just as we did in designing our command-line interface. That means that the user will be prompted to input his or her name and the program will respond by displaying a greeting. We will call our GUI GreeterGUI, to suggest its interdependence with the same Greeter computational object that we used with the command-line interface.

For this simple application, our GUI will make use of the following components:

  • A JTextField will be used to accept user input.

  • A JTextArea will serve to display the program's output.

  • A JButton will allow the user to request the greeting.

  • A JLabel will serve as a prompt for the JTextField.

Try the GreeterGUI program below.

FigureĀ 4.4.8 shows some of the constructors and public methods for the JTextArea, JTextField, JButton, and JLabel components.

Figure 4.4.8. Public methods and constructors for basic Swing components.

The following code segments illustrate how to use these constructors to create instances of these components:

// Declare instance variables for the components
private JLabel prompt;
private JTextField inField;
private JTextArea display;
private JButton goButton;

// Instantiate the components
prompt = new JLabel("Please type your name here: ");
inField = new JTextField(10);   // 10 chars wide
display = new JTextArea(5, 20);// 5 rows x 20 columns
goButton = new JButton("Click here for a greeting!");

For this example, we use some of the simpler constructors. Thus, we create a JTextField with a size of 10. That means it can display 10 characters of input. We create a JTextArea with 5 rows of text, each 20 characters wide. We create a JButton with a simple text prompt meant to inform the user of how to use the button.

Subsection 4.4.5 Adding GUI Components to a Top-Level Window

Now that we know how to create GUI components, the next task is to add them to the top-level window. Java's Container class has several add() methods that can be used to insert components into the container:

add(Component comp)  // add comp to end of  container
add(Component comp, int index)  // add comp at index
add(String region, Component comp) //add comp at region

The particular add() method to use depends on how we want to arrange the components in the container. The layout of a container is controlled by its default layout manager, an object associated with the container that determines the sizing and the arrangement of its contained components. We used the default FlowLayout where components are added in order:

setLayout(new FlowLayout(FlowLayout.LEFT));

Subsection 4.4.6 Controlling the GUI's Action

Now that we know how to place all the components on the GUI, we need to design the GUI's controls. As mentioned earlier, GUIs use a form of event-driven programming. Anything that happens when you are using a computerā€”every keystroke and mouse movementā€”is classified as an event. As FigureĀ 4.4.9 illustrates, events are generated by the computer's hardware and filtered up through the operating system and the application programs. Events are handled by special objects called listeners. A listener is a specialist that monitors constantly for a certain type of event. Some events, such as inserting a CD in the CD-ROM drive, are handled by listeners in the operating system. Others, such as typing input into a Web page or a Word document, are handled by listeners in a piece of application software, such as a browser or a word processor.

Figure 4.4.9. Java's event model.

In an event-driven programming model, the program is controlled by an event loop. That is, the program repeatedly listens for events, taking some kind of action whenever an event is generated. In effect, we might portray this event loop as follows:

Repeat forever or until the program is stopped
    Listen for events
    If event-A occurs, handle it with event-A-handler
    If event-B occurs, handle it with event-B-handler
     ...

The event loop listens constantly for the occurrence of events and then calls the appropriate object to handle each event.

FigureĀ 4.4.10 shows some of the main types of events in the java.awt.event package. In most cases, the names of the event classes are suggestive of their roles. Thus, a MouseEvent occurs when the mouse is moved. A KeyEvent occurs when the keyboard is used. The only event that our program needs to listen for is an ActionEvent, the type of event that occurs when the user clicks the JButton.

Figure 4.4.10. Java's event hierarchy.

When the user clicks the JButton, Java will create an ActionEvent object. This object contains important information about the event, such as the time that the event occurred and the object, such as a JButton, that was the locus of the event. For our application, when the user clicks the JButton, the program should input the user's name from the JTextField and display a greeting, such as ā€œHi John nice to meet youā€ in the JTextArea. That is, we want the program to execute the following code segment:

String name = inField.getText();
display.setText("Hello " + name + "\n");

The first line uses the JTextField.getText() method to get the text that the user typed into the JTextField and stores it in a local variable, name. The second line sets the text in the JTextArea to the concatenation of "Hello " + the value in the name variable + a new line ("\n").

In this example, we have used a couple of the standard public methods of the JTextField and JTextArea classes. For our simple GUI, the methods described in FigureĀ 4.4.8 will be sufficient for our needs. However, if you would like to see the other methods available for these and other Swing components, you should check Ch. 13 and Java's online Swing documentation.

Subsection 4.4.7 The ActionListener Interface

Given that the code segment just described will do the task of greeting the user, where should we put that code segment in our program? We want that code segment to be invoked whenever the user clicks on the goButton. You know enough Java to understand that we should put that code in a Java method. However, we need a special method in this case, one that will be called automatically by Java whenever the user clicks that button. In other words, we need a special method that the button's listener knows how to call whenever the button is clicked.

Java solves this problem by letting us define a pre-selected method that can be associated with the goButton. The name of the method is actionPerformed() and it is part of the ActionListener interface. In this case, an interface is a special Java class that contains only methods and constants (final variables). It cannot contain instance variables. (Be careful to distinguish this kind of interface, a particular type of Java class, form the more general kind of interface, whereby we say that a class's public methods make up its interface to other objects.) Here's the definition of the ActionListener interface:

public abstract interface ActionListener
                                extends EventListener
{    
  public abstract void actionPerformed(ActionEvent e);
}

This resembles a class definition, but the keyword interface replaces the keyword class in the definition. Note also that we are declaring this interface to be abstract. An abstract interface or abstract class is one that contains one or more abstract methods. An abstract method is one that consists entirely of its signature; it lacks an implementationā€”that is, it does not have a method body. Note that the actionPerformed() method in ActionListener places a semicolon where its body is supposed to be.

Declaring a method abstract means that we are leaving its implementation up to the class that implements it. This way, its implementation can be tailored to a particular context, with its signature specifying generally what the method should do. Thus, actionPerformed() should take an ActionEvent object as a parameter and perform some kind of action.

What this means, in effect, is that any class that implements the actionPerformed() method can serve as a listener for ActionEvent s. Thus, to create a listener for our JButton, all we need to do is give an implementation of the actionPerformed() method. For our program, the action we want to take when the goButton is clicked, is to greet the user by name. Thus, we want to set things up so that the following actionPerformed() method is called whenever the goButton is clicked:

public void actionPerformed(ActionEvent e)
{ 
  String name = inField.getText();
  display.setText("Hello " + name + "\n");
 }

In other words, we place the code that we want executed when the button is clicked in the body of the actionPerformed() method. That explains what gets done when the button is clickedā€”namely, the code in actionPerformed() will get executed. But it doesn't explain how Java knows that it should call this method in the first place. To set that up we must do two further things. We must place the actionPerformed() method in our GreeterGUI class, and we must tell Java that GreeterGUI will be the ActionListener for the goButton.

The following stripped-down version of the GreeterGUI class illustrates how we put it all together:

public class GreeterGUI extends Frame
                               implements ActionListener
{ 
  private JButton goButton;
  private JTextfield inField;
  private JTextArea display;
  ...
  public GreaterGUI()
  {  ...
     goButton = new JButton("Click here for a greeting!");
     goButton.addActionListener(this);
     ...
  }
  ...
  public void actionPerformed(ActionEvent e)
  {   
    String name = inField.getText();
    display.setText("Hello " + name + "\n");
  }
  ...
}

First, we declare that GreeterGUI implements the ActionListener interface in the class header. This means that the class must provide a definition of the actionPerformed() method, which it does. It also means that GreeterGUIisaActionListener. So SimpleGUI is both a JFrame and an ActionListener.

Second, note how we use the addActionListener() method to associate the listener with the goButton:

goButton.addActionListener(this)

The this keyword is a self-referenceā€”that is, it always refers to the object in which it is used. It's like a person referring to himself by saying ā€œIā€. When used here, the this keyword refers to this GreeterGUI. In other words, we are setting things up so that the GreeterGUI will serve as the listener for action events on the goButton.

ListingĀ 4.4.14 gives the complete source code for our GreeterGUI interface.

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

public class GreeterGUI extends JFrame implements ActionListener
{ 
  // Declare instance variables for the components
  private JLabel prompt;
  private JTextField inField;
  private JTextArea display;
  private JButton goButton;
    
  /* Constructor: set up GUI */
  public GreeterGUI()
  {
    // Instantiate the GUI components
    prompt = new JLabel("Please type your name here: ");
    inField = new JTextField(10);   // 10 chars wide
    display = new JTextArea(5, 20);// 5 rows x 20 columns
    goButton = new JButton("Click here for a greeting!");
    
    // add the components
    setLayout(new FlowLayout(FlowLayout.LEFT));  
    add(prompt);
    add(inField);
    add(goButton);
    add(display);
  
    // make go button clickable
    goButton.addActionListener(this);
    
    // widthxheight of window
    setSize(400,200);
    // display the JFrame    
    setVisible(true); 
  } 

  /* actionPerformed is called when the button is clicked */
  public void actionPerformed(ActionEvent e)
  { 
    String name = inField.getText();
    display.setText("Hello " + name + "\n");
  } 
}
Listing 4.4.14. Definition of the GreeterGUI class.

Subsection 4.4.8 Using the GUI in a Java Application

As you know, a Java application is a stand alone program, one that can be run on its own. We have designed our GUI so that it can easily be used with a Java application. One way to use the GUI in an application is simply to create an instance in a main() method. The main() method can be placed in the GreeterGUI class itself or in a separate class. Here's an example with the main in a separate class:

public class Main
{  public static void main(String args[])
   {
      GreeterGUI = new GreeterGUI();
   }
}

The main() method creates an instance of GreeterGUI. If you prefer, this same main() method can be incorporated directly into the GreeterGUI class.

Exercises Self-Study Exercises

1. GreeterGUI.

There is a simple modification that we can make to GreeterGUI. The JTextField can serve both as an input element and as a control element for action events. An ActionEvent is generated whenever the user presses the Return or Enter key in a JTextField. Of course, it will be necessary to designate the inField as an ActionListener in order to take advantage of this feature. Fork the following repl to make the appropriate changes so that the inField can function as both a control and input element. Also, change the welcome message.

You have attempted of activities on this page.