First, what is an event?
Where do you find classes related to events?
Classes related to events:
We have already seen, for example, that a JButton can generate an ActionEvent that must be handled by an ActionListener instance.
Let us review this example once more:
JButton b = new JButton ("Quit");
public interface ActionListener extends EventListener { public abstract void actionPerformed (ActionEvent a); }
b.addActionListener (this); // "this" refers to the frame.
b.addActionListener ( new ActionListener () { public void actionPerformed (ActionEvent a) { // ... do whatever needs to be done ... } } );
What really happens when a button is pressed?
How to work with Swing events without losing your mind:
Some general information about "event" classes:
Next, a tour of some "listeners":
public interface ActionListener extends EventListener { public abstract void actionPerformed (ActionEvent a); }
public interface MouseListener extends EventListener { public abstract void mouseClicked (MouseEvent m); public abstract void mouseEntered (MouseEvent m); public abstract void mouseExited (MouseEvent m); public abstract void mousePressed (MouseEvent m); public abstract void mouseReleased (MouseEvent m); }
public interface MouseMotionListener extends EventListener { public abstract void mouseDragged (MouseEvent m); public abstract void mouseMoved (MouseEvent m); }
public interface KeyListener extends EventListener { public abstract void keyPressed (KeyEvent k); public abstract void keyReleased (KeyEvent k); public abstract void keyTyped(KeyEvent k); }
public interface ItemListener extends EventListener { public abstract void itemStateChanged (ItemEvent i); }
public interface FocusListener extends EventListener { public abstract void focusGained (FocusEvent f); public abstract void focusLost (FocusEvent f); }
public interface AdjustmentListener extends EventListener { public abstract void adjustmentValueChanged (AdjustmentEvent a); }
public interface WindowListener extends EventListener { public abstract void windowOpened (WindowEvent w); public abstract void windowClosed (WindowEvent w); public abstract void windowClosing(WindowEvent w); public abstract void windowIconified (WindowEvent w); public abstract void windowDeiconified (WindowEvent w); public abstract void windowActivated (WindowEvent w); public abstract void windowDeactivated (WindowEvent w); }
public interface TextListener extends EventListener { public abstract void textValueChanged (TextEvent t); }
Note:
It is possible to define classes inside top-level classes. One motivation for introducing this syntax was to improve event-handling.
There are four types of nested classes:
We will use a somewhat contrived (but simple!) example to illustrate:
Here is what we want the frame to look like:
Here is the code: (source file)
import java.awt.*; import javax.swing.*; import java.awt.event.*; class NewFrame extends JFrame implements ActionListener { // Data. JButton quitB; // Quit button. JButton helloB, worldB; // Two silly buttons. // Constructor. public NewFrame (int width, int height) { // Set the title and other frame parameters. this.setTitle ("Two button example"); this.setResizable (true); this.setSize (width, height); // We'll use a flowlayout Container cPane = this.getContentPane(); cPane.setLayout (new FlowLayout()); // Quit button quitB = new JButton ("Quit"); quitB.setBackground (Color.red); quitB.addActionListener (this); cPane.add (quitB); // "Hello" button helloB = new JButton ("Hello"); helloB.setBackground (Color.green); helloB.addActionListener (this); cPane.add (helloB); // "World" button worldB = new JButton ("World"); worldB.setBackground (Color.green); worldB.addActionListener (this); cPane.add (worldB); // Show the frame. this.setVisible (true); } // Need this method for the ActionListener interface. public void actionPerformed (ActionEvent a) { // Get the button string. String s = a.getActionCommand(); if (s.equalsIgnoreCase ("Quit")) System.exit(0); else if (s.equalsIgnoreCase ("Hello")) { System.out.print ("Hello "); } else if (s.equalsIgnoreCase ("World")) { System.out.println ("World!"); } } } // End of class "NewFrame" public class TwoButton { public static void main (String[] argv) { NewFrame nf = new NewFrame (300, 200); } }
The above approach of having the frame implement the listeners is undesirable for many reasons:
To solve this problem, Java provides the use of so-called local classes:
public NewFrame (int width, int height) { // Set the title and other frame parameters. this.setTitle ("Two button example"); this.setResizable (true); this.setSize (width, height); // We'll use a flowlayout Container cPane = this.getContentPane(); cPane.setLayout (new FlowLayout()); // Quit button quitB = new JButton ("Quit"); quitB.setBackground (Color.red); // Local class definition INSIDE a method. class QuitButtonListener implements ActionListener { public void actionPerformed (ActionEvent a) { System.exit (0); } } // Instantiate local class. QuitButtonListener qListener = new QuitButtonListener(); // Add the listener to the button. quitB.addActionListener (qListener); // Finally, add the button to the frame. cPane.add (quitB); // The rest is the same as before ... }
Note:
class NewFrame extends JFrame implements ActionListener { // ... // Constructor. public NewFrame (int width, int height) { // ... // Create a local class. QuitActionListener is our name. class QuitActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { System.exit(0); // Action required for quit. } } // ... } }
// Instantiate local class. QuitActionListener qListener = new QuitActionListener(); // Add the listener to the button. quitB.addActionListener (qListener);
quitb.addActionListener (new QuitActionListener());
Next, we will make some modifications:
Here is the code: (source file)
class NewFrame extends JFrame { // Note: Frame does not implement ActionListener anymore // Data. JButton quitB; // Quit button. JButton helloB, worldB; // Two silly buttons. String helloStr = "Hello "; String worldStr = "World!"; // Constructor. public NewFrame (int width, int height) { // Set the title and other frame parameters. this.setTitle ("Two button example"); this.setResizable (true); this.setSize (width, height); // We'll use a flowlayout Container cPane = this.getContentPane(); cPane.setLayout (new FlowLayout()); // Quit button quitB = new JButton ("Quit"); quitB.setBackground (Color.red); // Local listener: class QuitActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { System.exit (0); } } quitB.addActionListener (new QuitActionListener()); cPane.add (quitB); // "Hello" button helloB = new JButton ("Hello"); helloB.setBackground (Color.green); // Listener: class HelloActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { // Note: we are accessing top-level variable. System.out.print (helloStr); } } helloB.addActionListener (new HelloActionListener()); cPane.add (helloB); // "World" button: code is similar ... } } // End of class "NewFrame"
class NewFrame extends JFrame { // ... String helloStr = "Hello "; String worldStr = "World!"; public NewFrame (int width, int height) { // ... class HelloActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { // Note: we are accessing top-level variable. System.out.print (helloStr); } } // ... }Note:
Next, we will continue our contrived example by printing something to System.out when a mouse-click occurs.
Again, we will use a local class: (source file)
public NewFrame (int width, int height) { // Set the title and other frame parameters. this.setTitle ("Two canvas example"); this.setResizable (true); this.setSize (width, height); // We'll use a flowlayout Container cPane = this.getContentPane(); cPane.setLayout (new FlowLayout()); // Quit button quitB = new JButton ("Quit"); quitB.setBackground (Color.red); // Local listener: class QuitActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { System.exit (0); } } quitB.addActionListener (new QuitActionListener()); cPane.add (quitB); // "Hello" button helloB = new JButton ("Hello"); helloB.setBackground (Color.green); // Listener: class HelloActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { // Note: we are accessing a top-level variable. System.out.print (helloStr); } } helloB.addActionListener (new HelloActionListener()); cPane.add (helloB); // "World" button worldB = new JButton ("World"); worldB.setBackground (Color.green); // Listener: class WorldActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { System.out.print (worldStr); } } worldB.addActionListener (new WorldActionListener()); cPane.add (worldB); // Deal with mouse clicks. class FrameMouseListener implements MouseListener { public void mouseClicked (MouseEvent m) { System.out.println ("Mouse click!"); } // Empty methods - to complete interface. public void mouseEntered (MouseEvent m) {} public void mouseExited (MouseEvent m) {} public void mousePressed (MouseEvent m) {} public void mouseReleased (MouseEvent m) {} } // Add to frame. cPane.addMouseListener (new FrameMouseListener()); // Show the frame. this.setVisible (true); }
Recall that, even though we only wanted to handle mouseClicked we still had to provide (empty) implementations for other mouse methods like mouseDragged:
class FrameMouseListener implements MouseListener { // ... public void mouseClicked (MouseEvent m) { // ... } // Empty methods - to complete interface. public void mouseEntered (MouseEvent m) {} public void mouseExited (MouseEvent m) {} public void mousePressed (MouseEvent m) {} public void mouseReleased (MouseEvent m) {} }
Java often provides special "adapter" classes to relieve this effort:
// Deal with mouse clicks. class FrameMouseListener extends MouseAdapter { public void mouseClicked (MouseEvent m) { System.out.println ("Mouse click!"); } // Don't need these: // public void mouseEntered (MouseEvent m) {} // public void mouseExited (MouseEvent m) {} // public void mousePressed (MouseEvent m) {} // public void mouseReleased (MouseEvent m) {} }
Recall the listener for the "quit" button defined earlier:
class NewFrame extends JFrame { // ... // Constructor. public NewFrame (int width, int height) { // ... // Create a local class for the quit button. class QuitActionListener implements ActionListener { public void actionPerformed (ActionEvent a) { System.exit(0); } } // Pass an instance to the button. quitB.addActionListener (new QuitActionListener()); // ... } }
Here, we defined a class and used it only once, wasting a name.
Java provides the ability to define a class right where it's needed - in creating an instance: (source file)
class NewFrame extends JFrame { // ... public NewFrame (int width, int height) { // ... // Quit button quitB = new JButton ("Quit"); quitB.setBackground (Color.red); // Create an implementation right in the method call: quitB.addActionListener ( new ActionListener() { public void actionPerformed (ActionEvent a) { System.exit (0); } } ); cPane.add (quitB); // ... } } // End of class "NewFrame"
Note:
quitb.addActionListener ( // This is the parameter of addActionListener: new ActionListener() // A new instance of ActionListener. // This is the body of the class: { public void actionPerformed (ActionEvent a) { System.exit(0); } } ); // End of parameter list.
// Deal with mouse clicks. cPane.addMouseListener ( new MouseAdapter () { public void mouseClicked (MouseEvent m) { System.out.println ("Mouse click!"); } } );
Exercise 10.1: Recall that we implemented an Enumeration for linked lists by having the linked list class implement the Enumeration interface:
public interface Enumeration { public abstract boolean hasMoreElements(); public abstract Object nextElement(); }
class LinkedList implements Enumeration { // ... public boolean hasMoreElements () { // ... } public Object nextElement() { // ... } public Enumeration getEnumeration () { // ... } } // End of class "LinkedList"
For completeness, we will now consider the remaining two kinds of inner classes:
We will present the material via examples:
class A { // A static member class. static class B { int x; // Data. public B (int i) // Constructor. { x = i; } public void print () // A method. { System.out.println ("B: x=" + x); } } } public class TestStaticLocal { public static void main (String[] argv) { // Create an instance of B. A.B b = new A.B (1); b.print(); // Prints "1". // Create another instance of B. A.B b2 = new A.B (2); b2.print(); // Prints "2". } }
class A { class B { int x; // Data. public B (int i) // Constructor. { x = i; } public void print () // A method. { System.out.println ("B: x=" + x); } } } public class TestLocal { public static void main (String[] argv) { A.B b = new A.B (1); // Does not compile. b.print(); } }
class A { // Instance data in A int y; // Constructor for A. public A (int y) { this.y = y; } // Instance member class: class B { int x; // Data. public B (int i) { // Constructor. x = i; } public void print () // A method. { System.out.println ("B: x=" + x + ", y=" + y); } } } public class TestLocal2 { public static void main (String[] argv) { // Create an instance of A first. A a = new A (1); // Now create an "associated" instance of B. // Note the strange syntax! A.B b = a.new B (2); b.print(); // Prints "B: x=2, y=1" } }