Monkeys at Keyboards: The Javanomicon
© Michael James Heron
Topic: Java Programming
Level: 2
Version: delta

In fact, the mere act of opening the box will determine the state of the cat, although in this case there were three determinate states the cat could be in: these being Alive, Dead, and Bloody Furious.
Terry Pratchett

26 - More Listener Objects

PreviousTable of ContentsNext
Forum


Chapter Objectives

By the end of this chapter, the reader will be able to:

  • make use of mouse events within their Java programs.
  • make use of keyboard events within their Java programs.


26.1

Introduction

  • We've learned how to write applets.
  • We've learned how to write applications.
  • We've learned how to build graphical user interfaces.
  • We've learned about data structures.
  • We've learned about File IO.
  • We've learned how to make use of graphics and sound

And that's only a small taste of what we've accomplished. However, in the process we've neglected a number of the peripheral areas of Java in order to focus on those parts of the language that are directly related to the subjects we were investigating.

For the programs we have written, this hasn't posed a serious problem... or even a problem at all. It becomes a problem later, when we realise there are some programs that we simply cannot write because we have never covered the concepts.

In this chapter, we're going to look at some of the other Listener objects provided by Java. We can use these to catch some interesting behaviour, such as that triggered by the mouse and by the keyboard.

26.2

Listener Objects

There are an enormous number of listener objects available as part of the Java framework. We've seen a few of these in the form of ActionListener, AdjustmentListener and ItemListener. But there are more, so many more... and these range from the extremely useful (such as the ones we'll discuss in this chapter) to the exceptionally specialised (such as TreeNodeListener).

The number of events triggered by a standard Java program are staggering... we make use of listener objects to restrict and manage this complexity. We have to specifically indicate our interest in the occurrence of a particular kind of event associated with a particular kind of component - Java does the rest, but it needs us to tell it in what we're interested. The benefit of this is that we can deal only with those events that have a direct influence on the way our programs work.

The downside is that unless we know what we're looking for, we may miss a lot of neat events that we could use to make our programs more effective or simply more interesting.

26.3

Mouse Events

Your mouse is a constant source of events in a Java program. It is spewing them out, dozens every minute... but since we've never been interested in them before, we've never done anything about them. Your mouse triggers events when:

  • A button is clicked
  • A button is held
  • A button is released
  • The mouse pointer enters the bounds of a component
  • The mouse pointer releases the bounds of a component

There are other situations, and we'll look at those in the course of this chapter.

We can create adapters to deal with each of these events in the way we discussed in the last chapter - we're not going to do that here. We're going to implement the functionality within our application/applet, just like in the Good Old Days so as not to confuse form with function.

We need to implement an interface to make use of mouse events - this interface is called MouseListener. Individual components must register their interest in mouse events by using the addMouseListener method.

Whichever object implements the listener interface must provide code for the following methods:

MouseListener Methods
Fig 26.1: MouseListener Methods

Each of these methods takes a parameter of type MouseEvent.

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

public class MouseListenerExample extends JFrame implements MouseListener
{
JLabel myLabel;
JButton myButton;

public static void main (String args[]) {
MouseListenerExample main = new MouseListenerExample();
main.setSize (300, 300);
main.setVisible (true);
}


public MouseListenerExample() {
myLabel = new JLabel ("Bing!");
myLabel.addMouseListener (this);
myButton = new JButton ("Press Me!");
add (myLabel, BorderLayout.CENTER);
add (myButton, BorderLayout.SOUTH);
}

public void mouseReleased (MouseEvent e) {
}


public void mousePressed (MouseEvent e) {
}


public void mouseExited (MouseEvent e) {
}


public void mouseEntered (MouseEvent e) {
}


public void mouseClicked (MouseEvent e) {
}

}

MouseEvents are slightly more complex than the other event objects we've looked at because they carry some more information with them. This information relates to the state of the mouse at the point the event was triggered. We make use of the MouseEvent object that gets passed to each handler method to query this information to ensure that our code behaves properly.

The MouseEvent object contains two methods of particular note: getX, and getY. These methods return the relative X and Y co-ordinates of the mouse when the event was triggered. For example, we can modify the code of mouseEntered to set the text of the label to the entry point for the pointer:


public void mouseEntered (MouseEvent e) {
myLabel.setText ("Pointer has entered my bounds. Entered at position " + "X: "
+ e.getX() + ", Y: " + e.getY());
}

MouseEvent also has a method called getButton that returns which button was pressed... it returns a different value for the left, middle and right buttons, so you can ensure that the correct functionality is triggered when one is pressed. We check the return value of getButton against one of the predefined constants in the MouseEvent class:


public void mouseClicked (MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
myLabel.setText ("The left button was just clicked.");
}

else if (e.getButton() == MouseEvent.BUTTON2) {
myLabel.setText ("The middle button was just clicked.");
}

else if (e.getButton() == MouseEvent.BUTTON3) {
myLabel.setText ("The right button was just clicked.");
}

}

Sometimes we don't want to find out about a single button click - we only want double clicks. There's a method in the MouseEvent object called getClickCount that returns how many times the button was clicked in a set interval. If the integer returned from this is two, it's a double-click:


public void mouseClicked (MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1&& e.getClickCount() == 2)
{
myLabel.setText ("The left button was just double-clicked.");
}

}

The MouseListener is just one of a range of listener objects that relate to mouse movements. They all work in the same way, which indeed is the same way as all our other listener objects that we've used up until now.

26.4

MouseMotionListener

The MouseListener interface allows us to catch one subset of the events that go with a mouse, and these are the events that are related to a particular component. Sometimes we just want to catch the events that go with mouse movements in general, without them being associated with any given component. We use a MouseMotionListener for this.

The interface for MouseMotionListener requires two methods to be implemented:

MouseMotionListener Methods
Fig 26.2: MouseMotionListener Methods

As with a MouseListener, these methods take a parameter of type MouseEvent.

We use addMouseMotionListener to register our interest in these events, but unlike MouseListener we register our interest on an applet/application as a whole in the same way we did with addWindowListener way back in the chapter on free standing applications:


public MouseMotionExample() {
...
addMouseMotionListener (this);
...
}

We're going to look at a slightly longer example of this by writing an application that tracks the motion of our mouse with a pair of eyes:

Eyes Wide Shut
Fig 26.3: Eyes Wide Shut

This is a good way to get to grips with our mouse events, because it involves writing the code that ensures the eyes react to the pointer when it is in certain locations.

First, we should discuss how we can get the eyes to follow the mouse. This is easier than it sounds - we simply draw the pupils as close to the mouse pointer as we can.

Obviously this won't work without some restrictions, or the eyes would be glued to the mouse pointer in a most unappealing way. We have to provide some restrictive bounds within which the eyes can move freely. The greater white oval of the eye is fixed. It's only the black pupil that is going to move.

We've already seen how we can draw circles and ovals on the screen - in our first applet chapters and in a more recent chapter where we did some basic animation. We simply use the fillOval method of the Graphics class, which takes four parameters representing the bounds within which the oval will be drawn. It's just a case of deciding where we want them to be drawn, and how big they're going to be:

Drawing the Ovals of the Eyes
Fig 26.4: Drawing the Ovals of the Eyes

Once we've drawn these, we need to decide on some bounding rectangle within these larger ovals of the eyes that restrict the movement of the pupil. We can't simply use the same bounds because then the pupils will move outside the oval of the eyes, which really isn't what we want. Instead we must provide some values that define a bounding rectangle within each of the ovals. The values for this must be chosen in such a way that we ensure that the black eyeball never leaves the white oval, even at the extreme edges.

Bounding Rectangles within the Ovals
Fig 26.5: Bounding Rectangles within the Ovals

The dimensions of this bounding rectangle are going to be dependant on how much movement we are willing to allow. In this case, we will set them as 10 length ways and 30 height ways. We also need to choose where they are going to be drawn, and planning will give us the following dimensions for the bounding rectangles for movement:

Bounding Rectangles Dimensions
Fig 26.6: Bounding Rectangles Dimensions

Now, here's the slightly tricky part - there's no point in actually drawing these rectangles because they won't stop the movement of the pupils. The best we can do is simply hard code the restrictions. Drawing it out simply makes it clearer to ourselves where they lie.

Okay, so let's start writing the code. We're going to do this as a JPanel within an application, so we need to write the framework for this and implement the MouseMotionListener interface:

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


public class MovingEyes extends JPanel implements MouseMotionListener {
int x, y;
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;

public MovingEyes() {
addMouseMotionListener (this);
}


public void mouseMoved (MouseEvent e) {
}


public void mouseDragged (MouseEvent e) {
}


public void paintComponent (Graphics g) {
super.paintComponent (g);
}

}

The code for dealing with the mouse events is going to be placed within mouseMoved, and the paintComponent method will do the actual drawing. We need some way of communicating between the two, so we'll provide two class wide variables of type integer, which we'll call x and y... and we'll set them to the appropriate value in the mouseMoved event before calling the repaint method:


public void mouseMoved (MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}

Okay... now we need to handle the drawing part. First of all, let's draw the larger white ovals of the eyes - that's a straightforward procedure that we already know all the values for:


public void paintComponent (Graphics g) {
super.paintComponent (g);
g.setColor (Color.WHITE);
g.fillOval (100, 100, 30, 50);
g.setColor (Color.WHITE);
g.fillOval (140, 100, 30, 50);
}

Compile and run, and we get two spooky white ovals on our applet. We really need to provide some definition around them, so we'll also draw an empty black oval around each:


public void paintComponent (Graphics g) {
super.paintComponent (g);
g.setColor (Color.WHITE);
g.fillOval (100, 100, 30, 50);
g.setColor (Color.BLACK);
g.drawOval (100, 100, 30, 50);
g.setColor (Color.WHITE);
g.fillOval (140, 100, 30, 50);
g.setColor (Color.BLACK);
g.drawOval (140, 100, 30, 50);
}

Okay, the next step is to add in our boundary values. We can't do this by drawing rectangles, we just need to store each of the values in an appropriate integer variable:

int maxht = 0, minht = 0; 
int maxlen1 = 0, maxlen2 = 0;
int minlen1 = 0, minlen2 = 0;
maxht = 135;
minht = 105;
minlen1 = 105;
maxlen1 = 115;
minlen2 = 145;
maxlen2 = 155;

Both pupils will have the same rules regarding their motion height-ways, so we use maxht and minht for both of them. Minht is the lowest that the X value can legitimately go, and maxht is likewise the highest.

We then use a separate minlen and maxlen for each, since their motion in the Y axis will be dependant on which eyeball they are in. All these variables go into the paint method.

We then need to do the boundary checking for each eyeball - this works exactly the same way as the boundary checking for the bouncing ball that we wrote in chapter (sound and vision).

We need to know where the top left hand corner of each pupil is - we'll need four class wide variables for this:

int x1 = 0, x2 = 0, y1 = 0, y2 = 0; 

These will change as the motion of the eyeball changes, in the same way as our animated bouncing ball.

Once we have these, we can implement the boundary checking. First in the X axis, because we need to check two separate values here:

if (x >= maxlen1) { 
x1 = maxlen1;
}

else if (x <= minlen1) {
x1 = minlen1;
}

else {
x1 = x;
}

if (x >= maxlen2) {
x2 = maxlen2;
}

else if (x <= minlen2) {
x2 = minlen2;
}

else {
x2 = x;
}

And then in the y axis, because both eyes will move the same way:

if (y <= minht) { 
y1 = minht;
y2 = minht;
}

else if (y >= maxht) {
y1 = maxht;
y2 = maxht;
}

else {
y1 = y;
y2 = y;
}

Finally, we just need to draw the eyeballs:

g.setColor (Color.BLACK); 
g.fillOval (x1, y1, 10, 10);
g.setColor (Color.BLACK);
g.fillOval (x2, y2, 10, 10);

So in the end, our finished paintComponent method looks like this:


public void paintComponent (Graphics g) {
int maxht = 0, minht = 0;
int maxlen1 = 0, maxlen2 = 0;
int minlen1 = 0, minlen2 = 0;
super.paintComponent (g);
maxht = 135;
minht = 105;
minlen1 = 105;
maxlen1 = 115;
minlen2 = 145;
maxlen2 = 155;
if (x >= maxlen1) {
x1 = maxlen1;
}

else if (x <= minlen1) {
x1 = minlen1;
}

else {
x1 = x;
}

if (x >= maxlen2) {
x2 = maxlen2;
}

else if (x <= minlen2) {
x2 = minlen2;
}

else {
x2 = x;
}

if (y <= minht) {
y1 = minht;
y2 = minht;
}

else if (y >= maxht) {
y1 = maxht;
y2 = maxht;
}

else {
y1 = y;
y2 = y;
}

g.setColor (Color.WHITE);
g.fillOval (100, 100, 30, 50);
g.setColor (Color.BLACK);
g.drawOval (100, 100, 30, 50);
g.setColor (Color.BLACK);
g.fillOval (x1, y1, 10, 10);
g.setColor (Color.WHITE);
g.fillOval (140, 100, 30, 50);
g.setColor (Color.BLACK);
g.drawOval (140, 100, 30, 50);
g.setColor (Color.BLACK);
g.fillOval (x2, y2, 10, 10);
}

We can then apply our new moving eyes JPanel to a proper application to give us the potential for Great Creepiness in our programs:

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


public class MouseMotionExample extends JFrame {
MovingEyes iCanSeeYou;

public static void main (String args[]) {
MouseMotionExample main = new MouseMotionExample();
main.setSize (300, 300);
main.setVisible (true);
}


public MouseMotionExample() {
iCanSeeYou = new MovingEyes();
add (iCanSeeYou, BorderLayout.CENTER);
}

}

How neat is that? I'll give you a clue - it's very neat!

26.5

MouseWheelListener

Finally on the subject of mouse listeners, we are going to look at the MouseWheelListener, which listens for mouse wheel events (no, seriously). We're going to alter our moving eyes a little so that we can change the size of the pupils by scrolling the mouse wheel.

Alas, MouseWheelListener is a very new addition to Java - it appears only in the 1.4 version of the development kit. If you are using an earlier version of Java, you will not be able to make use of it.

MouseWheelListener has only one method that it needs implemented, and that is the mouseWheelMoved method, which takes a parameter of MouseWheelEvent.

The MouseWheelEvent object has a useful method called getWheelRotation that returns how much the wheel was turned, and in what direction. If it returns a positive number, the wheel has been scrolled upwards. If it returns a negative number, the wheel has been scrolled downwards.

With this in mind, we're going to make a small change to our paintComponent method, in that we will no longer be drawing our pupils at size 10... instead we'll be drawing them according to the value of a class wide integer variable called size.

After implementing MouseWheelListener, we have only to register our interest in mouse wheel events, and add our required method to provide the required functionality. We register our interest on a JPanel as a whole.


public MovingEyes() {
addMouseMotionListener (this);
addMouseWheelListener (this);
size = 10;
}

And then implement the functionality in the mouseWheelMoved method. Our modified MovingEyes class now looks like this:

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

public class MovingEyes extends JPanel implements MouseMotionListener, MouseWheelListener
{
int x, y;
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
int size;

public MovingEyes() {
addMouseMotionListener (this);
addMouseWheelListener (this);
size = 10;
}


public void mouseMoved (MouseEvent e) {
x = e.getX();
y = e.getY();
repaint();
}


public void mouseDragged (MouseEvent e) {
}


public void paintComponent (Graphics g) {
int maxht = 0, minht = 0;
int maxlen1 = 0, maxlen2 = 0;
int minlen1 = 0, minlen2 = 0;
super.paintComponent (g);
maxht = 135;
minht = 105;
minlen1 = 105;
maxlen1 = 115;
minlen2 = 145;
maxlen2 = 155;
if (x >= maxlen1) {
x1 = maxlen1;
}

else if (x <= minlen1) {
x1 = minlen1;
}

else {
x1 = x;
}

if (x >= maxlen2) {
x2 = maxlen2;
}

else if (x <= minlen2) {
x2 = minlen2;
}

else {
x2 = x;
}

if (y <= minht) {
y1 = minht;
y2 = minht;
}

else if (y >= maxht) {
y1 = maxht;
y2 = maxht;
}

else {
y1 = y;
y2 = y;
}

g.setColor (Color.WHITE);
g.fillOval (100, 100, 30, 50);
g.setColor (Color.BLACK);
g.drawOval (100, 100, 30, 50);
g.setColor (Color.BLACK);
g.fillOval (x1, y1, size, size);
g.setColor (Color.WHITE);
g.fillOval (140, 100, 30, 50);
g.setColor (Color.BLACK);
g.drawOval (140, 100, 30, 50);
g.setColor (Color.BLACK);
g.fillOval (x2, y2, size, size);
}


public void mouseWheelMoved (MouseWheelEvent e) {
int notches = e.getWheelRotation();
int current = size;
current = current + notches;
size = current;
if (size < 3) {
size = 3;
}

if (size > 15) {
size = 15;
}

repaint();
}

}

And there we go - in addition to having eyes that follow our pointer around the screen, we have eyes that grow and shrink as we scroll the wheel. Neato!

26.6

Keyboard Events

Now that we've discussed how to make use of mouse events, we should look at the other main input device we have available - the keyboard.

Many Swing components are already keyboard friendly to a degree - for example, they allow for shortcut keys to be set via the setMnemonic method we discussed in chapter five. In most cases, there is little need to implement keyboard event handling since it is done automatically. We don't need to write handling code to allow us to type into a JTextField, for example.

In most cases, we only need to implement a key listener of some sort if we are writing an application that involves key action without it going through a component (like a game), or when we want an application to respond to non-alphanumeric keys, like the cursors or the function keys.

The interface we must implement to make use of keyboard events is called KeyboardListener. It requires the following methods to be implemented:

KeyListener Events
Fig 26.7: KeyListener Events

Each of these methods takes a KeyEvent parameter, which comes with a very useful method called getKeyCode. This returns an integer value that represents the key that has been pressed. For the alpha-numeric keys, this corresponds to their Unicode value, so it is possible to place it directly into a char variable.

The KeyEvent class also contains a number of very useful constants that we can compare against the returned value from getKeyCode:

getKeyCode Values
Fig 26.8: getKeyCode Values

And so on...

The Java documentation contains a fuller description for the key codes attached to the KeyEvent class.

We can use these key codes to implement any key functionality we want attached to our application. For example, for a simple typewriter application:


public void keyTyped (KeyEvent e) {
if (e.getKeyChar() == KeyEvent.VK_ENTER) {
text = text + "<br>";
}

else if (e.getKeyChar() == KeyEvent.VK_ESCAPE) {
text = "";
}

else {
text = text + e.getKeyChar();
}

output.setText ("<html>" + text + "</html>");
}

Sometimes we want to be able to have our applications respond differently depending on how long between keyboard presses. There's a method in KeyEvent called getWhen, and it returns the number of milliseconds since the start of the standard Unix epoch. The getWhen method is timestamped with the instant the KeyEvent was created.

Alas, there is a problem in the way key events are generated. Consider a simple example of a system that lets us setup a morse code message by tapping the space-bar. If we give it a short tap we should generate a dot. If we give it a long tap, we should generate a dash. A short tap is defined as a key press that lasts less than .2 seconds, and a long tap is anything else.

Our getWhen method should handle this for us, but alas, we get some strange behaviour. We get the time that a key was pressed by calling getWhen in the keyPressed method and storing it in a class-wide variable. When the key is released (which calls keyReleased) we then compare the time the key was pressed against the time it was released. Theoretically, that should give us the delay:

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


public class MorseCode extends JFrame implements KeyListener {
long then, now;
boolean pressing;
String text;
JLabel codeLabel;

public MorseCode() {
codeLabel = new JLabel ("");
addKeyListener (this);
add (codeLabel, BorderLayout.CENTER);
text = "";
}


public static void main (String args[]) {
MorseCode mainWindow = new MorseCode();
mainWindow.setSize (500, 500);
mainWindow.setVisible (true);
}


public void keyTyped (KeyEvent e) {
}

public void keyPressed (KeyEvent e) {
if (e.getKeyChar() != KeyEvent.VK_SPACE) {
return;
}

then = e.getWhen();
}


public void keyReleased (KeyEvent e) {
long delay;
delay = now - then;
if (e.getKeyChar() != KeyEvent.VK_SPACE) {
return;
}

if (delay < 200) {
text = text + ".";
}

else {
text = text + "-";
}

codeLabel.setText (text);
}

}

Alas, when we run this application, all we ever get are dots! Even when we hold the key for many seconds, it still generates a dot. How peculiar!

The reason for this may be found in the autorepeat mechanism provided by many operating systems. Hold down a key and it will cause the character to repeat until you release it. Alas, Java simulates this mechanism by calling keyPressed for every repeat character... which means the then timestamp gets set to when the last character was repeated. The delay between repeats is about fifty milliseconds, so it never draws a dash.

We deal with this by providing a simple mechanism... when we press a key for the first time, we set the time-stamp and set a boolean variable to true. We check when we enter the keyPressed method to see if this boolean variable is true. If it is, we simply return from the method.

In keyReleased, we reset this boolean variable back to false and then calculate the delay:


public void keyPressed (KeyEvent e) {
if (e.getKeyChar() != KeyEvent.VK_SPACE) {
return;
}

if (pressing == true) {
return;
}

pressing = true;
then = e.getWhen();
}


public void keyReleased (KeyEvent e) {
long delay;
now = e.getWhen();
if (pressing == false) {
return;
}

pressing = false;
delay = now - then;
if (delay < 200) {
text = text + ".";
}

else {
text = text + "-";
}

codeLabel.setText (text);
}

Now we've managed to work our way past the autorepeat mechanism and implement a simple morse code system.

26.7

Focus Events

When a key has been pressed and the appropriate listener object catches the event, the getSource method of the KeyEvent parameter will return the object that currently has focus. This is the case even if the current source object is not one we would normally associate with a key event.

This means that if you are basing your action on the source of the event it can be quite tricky to ensure a proper course of execution based on what you get out of getSource.

For example, consider a simple game where we have a selection of options and a game window:

Hypothetical Game Window
Fig 26.9: Hypothetical Game Window

If the user presses an arrow key when the game is over, we'd like it to move the focus between buttons. Otherwise we'd like it to control the action in the game window.

However, after having pressed the New button, it is that button that has focus... and so getSource of they key events will return that button as their source until the game window gains focus.

In most programs, this probably won't be a problem. However, in those that it will be, it is necessary for the application to ensure focus is reset properly. This is also a very useful technique for general user interface design. For example, when filling out an online questionnaire and finding that one value doesn't meet the required format, it is user friendly to ensure the offending JTextField has the focus so the user can type directly into it rather than have to find it and click on it themselves.

We can use the requestFocus method of a component to ensure it gains the focus in an application.

We can register interest in focus events by implementing FocusListener in our application. We register individual components as the source of our interest in the events. The FocusListener interface demands two methods be implemented

Focus Events
Fig 26.10: Focus Events

Both of these methods take a FocusEvent as their parameter.

Some objects do not gain the focus in an application/applet by default. For example, labels will never gain a focus by being clicked on unless you explicitly tell Java they should be a valid focus component. This can be done by calling setFocusable on them:

JLabel myLabel; 
myLabel = new JLabel ("Bing");
myLabel.setFocusable (true);

We only need register a FocusListener when we have some focus handling functionality that is out of the ordinary. For example, if we are displaying an animation we may want to halt the animation loop if the display window loses focus. We can do this by implementing the required code in the focusLost method, and then restarting the animation loop can be handled in the focusGained method.

26.8

Conclusion

Java is full of events. It triggers many more events than we can possibly deal with in the course of an application's lifetime. It provides us with a way of managing this information overload through the use of listener objects. We specifically register an interest in a particular kind of event through using the appropriate addXXXlistener method on the appropriate component. We then must provide the method code for dealing with these events.

Some of these events are tremendously complicated and require lots of understanding of the underlying structure of Java before they can be used properly. Most are sufficiently specialised that they are useful only for very specific situations and cannot be expanded into generally useful tools.

Mouse events and keyboard events are some of the more useful events that Java throws our way in the course of normal execution of a program. Mouse events in particular are triggered all the time - the sheer volume of events called means that dealing with all possible mouse events requires the implementation of no fewer than three interfaces.

Largely all event handling follows the same structure. We register an interest in a kind of event, we implement the interface, we provide the method handling bodies, and we're done. The interested reader is directed towards the Java document which is overflowing with examples of other listener objects to suit every occasion.

Further Reading

The following table details further reading on the topic in this chapter, and also any external resources that you may find useful.

ResourceDescription
Example Programs from this chapterThis is a zip file of all the programs shown in this chapter.

PreviousTable of ContentsNext

© 2004-2006 Michael James Heron