CSCI A201/A597

Lecture Notes Twenty-Three

Second Summer 2000


Add user interface components to a frame. Pages 414-423, 479-488.
Let's start from the standalone application that we developed yesterday. The one with the frame?

Yes. OK, here it is:
import javax.swing.*; 
import java.awt.event.*; 
       
class MyFrame extends JFrame {
  public MyFrame() {
    final int DEFAULT_WIDTH  = 300; 
    final int DEFAULT_HEIGHT = 300; 
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 
    WindowCloser w = new WindowCloser(); 
    addWindowListener(w); 
  } 
} 
       
class FrameTest {
  public static void main(String[] args) {
    MyFrame f = new MyFrame(); 
    f.setTitle("This is my title."); 
    f.show(); 
  } 
} 
       
class WindowCloser extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0); 
  } 
} 

You've changed a few things. Not much, just the name of a class.

And the name of the constructor, in all the invocations. Yes. Then I put this in a file called One.java, compiled it and run the class with the main method in it.

java FrameTest Yes.

OK, now let's add a few user interface components to this frame. We'll add a drawing surface and a text field.

Thus illustrating the same scheme of event-handling that we presented yesterday. Same scheme, different event sources.

Yes, but the mechanism is the same: ... define a listener, create it, register it with the source.

Then wait for the user. The user is always right.

Let's first deal with the drawing surface. You should never draw directly onto the surface of a frame.

Frames have been designed to arrange user interface components, such as buttons, menus, scroll bars, and so on. Drawing directly on the frame interferes with the display of the user interface components.

If you want to show graphics in a frame, you draw the graphics onto a separate component, ... and add that component to the frame.

The Swing user interface toolkit provides a special component, called JPanel, just for this purpose. A JPanel is completely blank, and you can draw onto it what you like.

Drawing on a panel is a bit different from drawing on an applet. To draw on an applet, you override the paint method.

To draw on a JPanel you instead override the paintComponent method. There is a second important difference;

... when implementing your own paintComponent method, you must call the paintComponent method of the superclass. This gives the superclass method a chance to erase the old contents of the panel.

Can you quickly remind me why we are doing all this? This what?

All this GUI and event-handling thing. To motivate you to study inheritance.

When will we study inheritance (also known as the class extension mechanism)? In A202. Or on our own.

If we had started with inheritance and spent a week on it, it may have looked a bit too abstract at first, no impetus. But now we see all of Java is built this way, so if we do understand the class extension mechanism we will be in much better shape.

Better positioned to understand the whole shebang. The whole contrivance.

Java. Yes.

Very good. Here's an outline of the paintComponent method.
import javax.swing.*; 
import java.awt.event.*; 
import java.awt.*;
       
class MyFrame extends JFrame {
  public MyFrame() {
    final int DEFAULT_WIDTH  = 300; 
    final int DEFAULT_HEIGHT = 300; 
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 
    WindowCloser w = new WindowCloser(); 
    addWindowListener(w); 
  } 
} 
       
class FrameTest {
  public static void main(String[] args) {
    MyFrame f = new MyFrame(); 
    f.setTitle("This is my title."); 
    f.show(); 
  } 
} 
       
class WindowCloser extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0); 
  } 
} 

class MyPanel extends JPanel {
    public void paintComponent(Graphics g) {
	super.paintComponent(g); 
	Graphics2D g2 = (Graphics2D)g; 
        // ... drawing instructions
    } 
}
The new is blue.

As always. To add a panel to a JFrame you need to know more about the structure of the frame surface.

Oh, no. Well, hang on.

The surface of a swing frame is covered with four panes, but I can't remember what they are.
  1. Root Pane
  2. Layered Pane
  3. Menu Bar
  4. Content Pane
  5. Glass Pane

I will never remember that. You don't need to. Make a note of it.

Three of them aren't even of interest, to most Java programmers. Yes, but you need to know about the content pane which holds the components that you want to display in the window.

OK, here's a picture of this:
Very nice.

(e) is transparent and captures mouse events. (b) holds (c) and (d) together.

(a) holds (e) and (b) together. Yes, that's it. To add a component,

... such as a panel or a button, ... to the content pane of a frame,

... you must first get a reference to the content pane object ... by calling the getContentPane method.

The method returns a reference of type Container. A Container is a window object that can contain other components.

Then you use the add method of the Container class to add your component. You need to specify both the component that you want to add,

... such as the pane, ... and the location where you want to add this component.

The content pane uses a border layout to arrange its components. You specify locations in a border layout as one of the five strings:
  • "Center"
  • "North"
  • "South"
  • "East" or
  • "West"

In chapter 12 you will see how to create other layouts. For now let's see how we put a panel into the "Center" are of the content pane.
import javax.swing.*; 
import java.awt.event.*; 
import java.awt.*;
       
class MyFrame extends JFrame {
  public MyFrame() {
    final int DEFAULT_WIDTH  = 300; 
    final int DEFAULT_HEIGHT = 300; 
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 
    WindowCloser w = new WindowCloser(); 
    addWindowListener(w); 
    MyPanel panel = new MyPanel(); 
    Container contentPane = getContentPane(); 
    contentPane.add(panel, "Center"); 
  } 
} 
       
class FrameTest {
  public static void main(String[] args) {
    MyFrame f = new MyFrame(); 
    f.setTitle("This is my title."); 
    f.show(); 
  } 
} 
       
class WindowCloser extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0); 
  } 
} 

class MyPanel extends JPanel {
    public void paintComponent(Graphics g) {
	super.paintComponent(g); 
	Graphics2D g2 = (Graphics2D)g; 
        // ... drawing instructions
    } 
}
That's it.

If you compile and run it now you won't see much of a difference. Let's add a text field to the frame then and read what the user types in it.
import javax.swing.*; 
import java.awt.event.*; 
import java.awt.*;
       
class MyFrame extends JFrame {
  JTextField textField; 
  public MyFrame() {
    final int DEFAULT_WIDTH  = 300; 
    final int DEFAULT_HEIGHT = 300; 
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 
    WindowCloser w = new WindowCloser(); 
    addWindowListener(w); 
    MyPanel panel = new MyPanel(); 
    Container contentPane = getContentPane(); 
    contentPane.add(panel, "Center"); 
    textField = new JTextField(); 
    contentPane.add(textField, "South"); 
  } 
} 
       
class FrameTest {
  public static void main(String[] args) {
    MyFrame f = new MyFrame(); 
    f.setTitle("This is my title."); 
    f.show(); 
  } 
} 
       
class WindowCloser extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0); 
  } 
} 

class MyPanel extends JPanel {
    public void paintComponent(Graphics g) {
	super.paintComponent(g); 
	Graphics2D g2 = (Graphics2D)g; 
        // ... drawing instructions
    } 
}
Now it looks much better and you can even type in the text field.

Let's add a listener that processes the user input. When the user hits the Enter key inside the text field the text field generates an action event.

Listening to an action event is a simpler task than listening to a mouse ... because the ActionListener interface has a single method:
public void actionPerformed(ActionEvent e); 

Which the listener is supposed to implement. The ActionEvent tells you which component sent the event and what kind of action the user wants to have carried out.

You will see many examples of action events in chapter 12. Right now we can ignore the contents of the event because we know it can only come from a single component, namely the textfield.

Can't the panel send action events? No, action events are sent only by buttons, lists, menu items, and text fields (see here).

As before we need to install a listener. We therefore define, create, and register it with the event source.
import javax.swing.*; 
import java.awt.event.*; 
import java.awt.*;
import java.awt.event.*; 
       
class MyFrame extends JFrame {
  JTextField textField; 
  public MyFrame() {
    final int DEFAULT_WIDTH  = 300; 
    final int DEFAULT_HEIGHT = 300; 
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 
    WindowCloser w = new WindowCloser(); 
    addWindowListener(w); 
    MyPanel panel = new MyPanel(); 
    Container contentPane = getContentPane(); 
    contentPane.add(panel, "Center"); 
    textField = new JTextField(); 
    contentPane.add(textField, "South"); 
    TextFieldListener listener = new TextFieldListener(); 
    textField.addActionListener(listener); 
  } 
} 
       
class FrameTest {
  public static void main(String[] args) {
    MyFrame f = new MyFrame(); 
    f.setTitle("This is my title."); 
    f.show(); 
  } 
} 
       
class WindowCloser extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0); 
  } 
} 

class MyPanel extends JPanel {
    public void paintComponent(Graphics g) {
	super.paintComponent(g); 
	Graphics2D g2 = (Graphics2D)g; 
        // ... drawing instructions
    } 
}

class TextFieldListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
	System.out.println("The user has pressed enter in the text field."); 
    } 
} 

So at this point we can detect when ... but not what

... the user has typed inside the text field. How do we get the contents of the text field?

How do we get the text field itself? We have two options: we can define the listener as an inner class as described in the book or,

... or use the action event to get to its source. Let's use the second method.

Here we go:
import javax.swing.*; 
import java.awt.event.*; 
import java.awt.*;
import java.awt.event.*; 
       
class MyFrame extends JFrame {
  JTextField textField; 
  public MyFrame() {
    final int DEFAULT_WIDTH  = 300; 
    final int DEFAULT_HEIGHT = 300; 
    setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 
    WindowCloser w = new WindowCloser(); 
    addWindowListener(w); 
    MyPanel panel = new MyPanel(); 
    Container contentPane = getContentPane(); 
    contentPane.add(panel, "Center"); 
    textField = new JTextField(); 
    contentPane.add(textField, "South"); 
    TextFieldListener listener = new TextFieldListener(); 
    textField.addActionListener(listener); 
  } 
} 
       
class FrameTest {
  public static void main(String[] args) {
    MyFrame f = new MyFrame(); 
    f.setTitle("This is my title."); 
    f.show(); 
  } 
} 
       
class WindowCloser extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0); 
  } 
} 

class MyPanel extends JPanel {
    public void paintComponent(Graphics g) {
	super.paintComponent(g); 
	Graphics2D g2 = (Graphics2D)g; 
        // ... drawing instructions
    } 
}

class TextFieldListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
	Object obj = e.getSource(); 
	System.out.println("Textfield: " + ((JTextField)obj).getText()); 
    } 
} 

Oh, no -- inheritance again! Yes, we need to study it.

Last updated: July 27, 2000 by Adrian German for A201