Events and Listeners

When a computer program is running, it often needs to respond to stimuli from outside. In the Java world, these are called events. In the general computer world, these are called interrupts. Examples of these include key presses, mouse movements and clicks, windows opening and closing. If your program is going to respond to any of these (it doesn't have to) it will have to tell the Java runtime system what it wants to do about it. As usual, this involves creating a class to represent the action you want to take. This classes are called listeners.

When we create GUI objects like buttons and windows, the user can interact with these. A GUI object is considered the source for the events, like mouse clicks, the occur when the user uses them. When an event occurs, the system needs to know what to do with it. It knows what object on the screen was the source of the event, but it doesn't know what you want to do with it.

Each GUI object has a method that handles this. You create a listener class for the GUI object. This class implements the interface for that objects listener. Each GUI object has its own listener interface. This is because each GUI object has its own set of events that it can respond to. For example, a window can be closed, iconified, zoomed to full screen, etc. A button can be clicked, but not iconified.

Once you create a listener object, you use the add listener method on the GUI object to register this listener with the Java runtime system. When an event occurs, the runtime system know which GUI object was the source of the event. It looks in a table to find the listener object that was registered. If there isn't one, the event is ignored. If there is one, then a method is called on the listener object that corresponds to the event. For example, on a WindowListener, there is a windowClosing() method that is called when the window is closed.

It is safe for the runtime to call this method because it knows that the object that was passed into the registration method on the GUI object had to implement the correct listener interface or the program wouldn't have compiled. event process

What makes all this work is polymorphism and the use of interfaces to define what kind of responses there can be. For example, when a button is pressed, this creates an event. The button object that you created has an instance variable in it to hold a listener object. You can define a listener class that implements the interface. You then define the methods to handle the event in any way you want.

This is a window event handler that will pop up a message box when the window is closed. We don't really need this, but in other projects, we may have to do some cleanup before the window closes.

 
// windowlisten.java: Create a frame to test window events
// since we are handling events and the pop up, we need
// these imports
import java.awt.*;
import java.awt.event.*;
import javax.swing.JOptionPane;

// a listener has methods that the runtime calls in response to events
public class windowlisten implements WindowListener {
	// events we don't care about but have to implement
	// to match the interface
	public void windowDeiconified(WindowEvent event) { }
	public void windowIconified(WindowEvent event) { }
	public void windowActivated(WindowEvent event) { }
	public void windowDeactivated(WindowEvent event) { }
	public void windowOpened(WindowEvent event) { }
	public void windowClosed(WindowEvent event) { }

  public void windowClosing(WindowEvent event) {
	JOptionPane.showMessageDialog(null, "Window is closing", "Window",JOptionPane.INFORMATION_MESSAGE);
  }
} // windowlisten

Now we need to make a window. We will create a class that extends JFrame. JFrame is a Java GUI class that creates a basic window. Our class will simply have a constructor that sets some properties on the window. Then it will create a listener object and register it with the runtime system.


// simplewindow.java: Create a frame 
// a frame is a basic window, just the title, close buttons, no contents
import javax.swing.JFrame;

public class simplewindow extends JFrame {
  
  public simplewindow() {
	// when one of these is created, that makes the window
	// now we set some of its properties
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // program ends when window closes
    setTitle("Test Window Event");  // text in title bar
    setSize(500, 500);  // size in pixels
    setVisible(true);   // make sure we can see it

	// create a listener to handle any events
	windowlisten w = new windowlisten(); 
    addWindowListener(w);  // Register listener
	// this could be compacted to
//    addWindowListener( new windowlisten() );  // Register listener
  }  // default constructor
} // simplewindow

Now here is the main class that starts everything up.


// listenexample.java: Create a window to test window events
import javax.swing.JFrame;

public class listenexample {

  public static void main(String[] args) {
	System.out.println("starting - setting up window");
    simplewindow frame = new simplewindow();   // this does all the work
	System.out.println("ending - window closed");
  }
} // listenexample
This creates the GUI components we are interested in.

Adapter classes

If we create a class to implement an interface, like the window listener above, we have to implement all the methods in the interface, even those we don't want to. The Java class libraries can help with this be providing adaptor classes. These are classes associated with interfaces. They implement the interface, providing empty methods for all the methods described in the interface. Now you can create a class that extends the adaptor and only have to override the methods that you are interested in. This reduces the amount of typing and makes the code smaller and easier to read.

The WindowAdapter class is a handy tool that implements the correct interface and has empty methods for all the required methods. Then when you create a new class like this, you just overload the methods you are interested in.

Here is the listener class from above using the adaptor.


// windowlisten.java: use the adapter class
// since we are handling events and the pop up, we need
// these imports
import java.awt.event.*;
import javax.swing.JOptionPane;

// a listener has methods that the runtime calls in response to events
public class windowlisten extends WindowAdapter {
  public void windowClosing(WindowEvent event) {
	JOptionPane.showMessageDialog(null, "Window is closing", "Window",JOptionPane.INFORMATION_MESSAGE);
  }
} // windowlisten

We don't need to make the listener class a separate class file. We can make define it as an inner class inside the simplewindow class like this.


public class simplewindow extends JFrame {
  
  public simplewindow() {
	// when one of these is created, that makes the window
	// now we set some of its properties
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // program ends when window closes
    setTitle("Test Window Event");  // text in title bar
    setSize(500, 500);  // size in pixels
    setVisible(true);   // make sure we can see it

	// Since this object is a listener, we can register it with the runtime
    addWindowListener( new windowlisten() );  // Register listener
  }  // default constructor

	private class windowlisten extends WindowAdapter {
  		public void windowClosing(WindowEvent event) {
			JOptionPane.showMessageDialog(null, "Window is closing", "Window",JOptionPane.INFORMATION_MESSAGE);
  		}
   } // windowlisten
  
} // simplewindow				
Note that the inner class is private. We are only using it inside the simplewindow class. When this class gets compiled, there will be two class files, one for the simplewindow class and one for the windowlisten class.

Now for the weird part. Since a class can extend one other class and implement multiple interfaces, we can create a class that extends JFrame to create the window and implements WindowListener so it can act as the listener for itself. In this case, we don't need the windowlisten class, just the simplewindow class like this.


// simplewindow.java: Create a frame 
// a frame is a basic window, just the title, close buttons, no contents
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class simplewindow extends JFrame implements WindowListener {
  
  public simplewindow() {
	// when one of these is created, that makes the window
	// now we set some of its properties
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // program ends when window closes
    setTitle("Test Window Event");  // text in title bar
    setSize(500, 500);  // size in pixels
    setVisible(true);   // make sure we can see it

	// Since this object is a listener, we can register it with the runtime
    addWindowListener(this);  // Register listener
  }  // default constructor

	// now we implement the interface routines like windowlisten did
	// events we don't care about but have to implement
	// to match the interface
	public void windowDeiconified(WindowEvent event) { }
	public void windowIconified(WindowEvent event) { }
	public void windowActivated(WindowEvent event) { }
	public void windowDeactivated(WindowEvent event) { }
	public void windowOpened(WindowEvent event) { }
	public void windowClosed(WindowEvent event) { }

  public void windowClosing(WindowEvent event) {
	JOptionPane.showMessageDialog(null, "Window is closing", "Window",JOptionPane.INFORMATION_MESSAGE);
  }
  
} // simplewindow

Now for the weirder part. We are creating the windowlisten class just to create exactly one instance and to immediately pass it into the addWindowListener() method on the simplewindow class. Java provides a way to do this with even less overhead than the inner class technique. We can define the class and create an instance inside the call to addWindowListener() method.


// simplewindow.java: Create a frame 
// a frame is a basic window, just the title, close buttons, no contents
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class simplewindow extends JFrame {
  
  public simplewindow() {
	// when one of these is created, that makes the window
	// now we set some of its properties
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // program ends when window closes
    setTitle("Test Window Event");  // text in title bar
    setSize(500, 500);  // size in pixels
    setVisible(true);   // make sure we can see it

	// Since this object is a listener, we can register it with the runtime
    addWindowListener( 
		new WindowAdapter() {
  			public void windowClosing(WindowEvent event) {
				JOptionPane.showMessageDialog(null, "Window is closing", "Window",JOptionPane.INFORMATION_MESSAGE);
  			}
   	} // windowlisten
					);  // Register listener
  }  // default constructor

  
} // simplewindow
The syntax here is a little unexpected.
new WindowAdapter() {
The call to new is expected and since we said we were defining the class and creating an instance, we should see the class name and the curly braces. But was is missing is the keyword class and there is the addition of the open and close parenthesis. The parentheses come from the way we normally use new. The class keyword is not used with new normally so we leave it off here.

When compiled, we still get an extra class file, but there is less coding overhead.