Adapters


Creating Event Listeners

This chapter will take us through the creation of several event listeners as we incrementally add functionality and interactivity to the MainFrame application of the previous chapter. Each program in this chapter extends directly or indirectly from MainFrame, inheriting functionality from its ancestors as well as providing additional functionality. This use of inheritance is convenient for this chapter’s purposes and for keeping individual source files small, but an equally viable alternative would be to combine all the inherited code into one source file.

image from book

 chap12.MainFrame    chap13.ListeningMainFrame0       chap13.ListeningMainFrame1          chap13.ListeningMainFrame2             chap13.ListeningMainFrame3                chap13.ListeningMainFrame4                   chap13.ListeningMainFrame5                      chap13.ListeningMainFrame6                         chap13.ListeningMainFrame7

image from book

Figure 13-4: Inheritance Hierarchy for the Examples Used in this Chapter

The steps to creating and registering a Listener are always the same and we will follow them with the creation of each class. The first examples will discuss these steps in great detail but as you gain familiarity with the process, we will move quicker and quicker through the steps until, with the creation of the last class, we will breeze quite speedily through the process. Along the way, we will explore various design approaches to implementing an event listener. These design approaches can be applied to any event/listener combination, the choice being determined by factors other than the type of event, listener or component involved.

Steps to Handling an Event:

  • Step 1. Application Behavior: Determine the desired application behavior from the user’s perspective.

  • Step 2. The Source: Determine the component that will be the source of the event.

  • Step 3. The Event: Determine and become familiar with the event type of interest making sure that the source component can respond to it.

  • Step 4. The Listener: Become familiar with the associated event listener.

  • Step 5. The Design Approach: Choose a design approach and implement the listener.

  • Step 6. Registration: Register the listener with the source component.

ListeningMainFrame0

First we will create the base class for this chapter, ListeningMainFrame0, which extends from MainFrame. ListeningMainFrame0 adds no event-handling code. It just customizes the title of the frame and provides an empty addListeners() method which its constructor calls. Each successive subclass will override the addListeners() method by calling super.addListeners() and adding new event-handling code.

Example 13.1: chap13.ListeningMainFrame0.java

image from book
 1     package chap13; 2 3     public class ListeningMainFrame0 extends chap12.MainFrame { 4 5       public ListeningMainFrame0() { 6         setTitle("Handling GUI Events"); 7         addListeners(); 8       } 9       protected void addListeners() {} 10      public static void main(String[] arg) { 11        ListeningMainFrame0 frame = new ListeningMainFrame0(); 12      } 13    }
image from book

ListeningMainFrame1

ListeningMainframe1 will handle the menubar.

Step 1. Application Behavior

When the user selects a menu item from the menu bar, we’ll cause a window to appear. It will report which menu item was selected.

Step 2. The Source

The source components are the four JMenuItems: menuItem1, menuItem2, menuItem3 and menuItem4.

Step 3. The Event

Remember that the event must be one to which the component can respond. Table 13-4 lists the EventListener registration methods available to JMenuItem as well as their associated Event types.

Table 13-4: Listeners and Event Types for JMenuItem

Listener Registration Method

Declaring Class

Corresponding Event Type

addMenuDragMouseListener()

JMenuItem

MenuDragMouseEvent

addMenuKeyListener()

JMenuItem

MenuKeyEvent

addActionListener()

AbstractButton

ActionEvent

addChangeListener()

AbstractButton

ChangeEvent

addItemListener()

AbstractButton

ItemEvent

addAncestorListener()

JComponent

AncestorEvent

addPropertyChangeListener()

JComponent

PropertyChangeEvent

addVetoableChangeListener()

JComponent

PropertyChangeEvent

addContainerListener()

Container

ContainerEvent

addComponentListener()

Component

ComponentEvent

addFocusListener()

Component

FocusEvent

addHierarchyBoundsListener()

Component

HierarchyEvent

addHierarchyListener()

Component

HierarchyEvent

addInputMethodListener()

Component

InputMethodEvent

addKeyListener()

Component

KeyEvent

addMouseListener()

Component

MouseEvent

addMouseMotionListener()

Component

MouseEvent

addMouseWheelListener()

Component

MouseEvent

Wow, that’s a lot of event types! Many of them are actually generated and sent to the JMenuItem when a user selects it. So what event type should we listen for when the user selects a menu item? Remember, we shouldn’t be interested in how the MenuItem was selected – just that it was selected. Referring back to the “Choosing the Right Event” section, we know that we should look for a semantic event. As with the “OK” button in a Dialog, ActionEvent is the event type that will cover all cases when the menu item is selected — whether by mouse press, key press or voice activation.

ActionEvent

Figure 13-5 shows the inheritance hierarchy for the ActionEvent class.

image from book

 java.util.EventObject    java.awt.AWTEvent       java.awt.event.ActionEvent

image from book

Figure 13-5: ActionEvent Inheritance Hierarchy

An ActionEvent is a semantic event that indicates that a component-defined action has occurred. Each component that can handle ActionEvents defines for itself what an “action” is. For example, buttons define the click of a mouse to be an action while text fields define the press of the enter-key to be an action. As figure 13-5 shows, Action-Event extends from AWTEvent, which is the root class for all AWT events. In addition to other methods, AWTEvent defines an “id” property. Event classes that extend AWTEvent define unique ids for each event the EventObject can represent, but it’s usually unnecessary to deal with event ids directly. Table 13-5 lists the one event id defined by ActionEvent.

Table 13-5: ActionEvent Event IDs

Event ID and Purpose

public static final int ACTION_PERFORMED = 1001

This indicates that a meaningful action occurred.

In addition to what it inherits from AWTEvent, the ActionEvent object defines several constants and contains several properties that may be of use in deciding how to handle the event. Some of these are listed in tables 13-6 and 13-7.

Table 13-6: ActionEvent Constants

Constant Name and Purpose

public static final int SHIFT_MASK

An indicator that the shift key was held down during the event.

public static final int CTRL_MASK

An indicator that the control key was held down during the event.

public static final int META_MASK

An indicator that the meta key was held down during the event.

public static final int ALT_MASK

An indicator that the alt key was held down during the event.

Table 13-7: ActionEvent Properties

Property Name and Purpose

public String getActionCommand()

Returns a string that can be used to identify the particular action to take. The value of this string is often populated automatically. In the case of a button or a menu item it is set to the “text” property of the button or menu item.

public long getWhen()

Returns the time the event occurred.

public int getModifiers()

Returns the bitwise-or of the modifier constants associated with the modifier keys held down during this action event. These constants are SHIFT_MASK, CTRL_MASK, META_MASK and ALT_MASK. You could use this to see if the control-key or shift-key was pressed with code such as:

 int mod = event.getModifiers(); if ((mod & ActionEvent.SHIFT_MASK) != 0) {   System.out.println("Shift key was down"); } if ((mod & ActionEvent.CTRL_MASK) != 0) {   System.out.println("Control key was down"); }

Components that respond to ActionEvents include AWT’s List, TextField, Button and MenuItem and Swing’s JFileChooser, JComboBox, JTextField, AbstractButton.

Step 4. The Listener

The associated listener is ActionListener which defines the one method listed in table 13-8. Figure 13-6 shows the inheritance hierarchy for the ActionListener interface.

Table 13-8: ActionListener Methods

Method Name and Purpose

public void actionPerformed(ActionEvent)

Called whenever an action occurs on the target of the listener.

image from book

 java.util.EventListener    java.awt.event.ActionListener

image from book

Figure 13-6: ActionListener Inheritance Hierarchy

When an action occurs on a component, an ActionEvent object is passed as a parameter to the actionPerformed method of every ActionListener that is registered with the component.

Step 5: The Design Approach: External Class

In example 13.2, we will create an external class called MyMenuActionListener that implements ActionListener. We will create only one instance of it and add it to all four of our JMenuItems. No matter which JMenuItem is selected, the same instance of MyMenuActionListener will be notified, so we will have to query the ActionEvent for its source to determine which JMenuItem was actually selected. Note that in this particular example, the only reason we get the actual JMenuItem (line 13) is so that we can invoke getText() on it. Invoking the getActionCommand() method directly on the ActionEvent object would have returned the same value as invoking getText() on the JMenuItem.

Example 13.2: chap13.MyMenuActionListener.java

image from book
 1     package chap13; 2 3     import java.awt.event.ActionEvent; 4     import java.awt.event.ActionListener; 5 6     import javax.swing.JMenuItem; 7     import javax.swing.JOptionPane; 8 9     public class MyMenuActionListener implements ActionListener { 10 11       public void actionPerformed(ActionEvent e) { 12         Object o = e.getSource(); 13         JMenuItem item = (JMenuItem)o; 14         JOptionPane.showMessageDialog( 15           item, 16           "You selected \"" + item.getText() + "\"."); 17      } 18    }
image from book

Step 6: Registration

In example 13.3, we register the listener with each JMenuItem via its addActionListener() method.

Example 13.3: chap13.ListeningMainFrame1.java

image from book
 1     package chap13; 2 3     import java.awt.event.ActionListener; 4 5     public class ListeningMainFrame1 extends ListeningMainFrame0 { 6 7       protected void addListeners() { 8         super.addListeners(); 9         ActionListener myMenuActionListener = new MyMenuActionListener(); 10        menuItem1.addActionListener(myMenuActionListener); 11        menuItem2.addActionListener(myMenuActionListener); 12        menuItem3.addActionListener(myMenuActionListener); 13        menuItem4.addActionListener(myMenuActionListener); 14      } 15      public static void main(String[] arg) { 16        ListeningMainFrame1 frame = new ListeningMainFrame1(); 17      } 18    }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

 javac –d classes -sourcepath src src/chap13/ListeningMainFrame1.java java –cp classes chap13.ListeningMainFrame1

ListeningMainFrame2

With ListeningMainFrame2, we will investigate mouse events.

Step 1: Application Behavior

We’ll use the event mechanism to capture all mouse activity in the text area and report it in the yellow label that says “Textarea events will display here” (eventLabel).

Step 2: The Source

The source component is the JTextArea, textArea.

Step 3: The Event

The event we need is MouseEvent whose inheritance hierarchy is shown in figure 13-7. A MouseEvent is a low-level event indicating that a mouse action occurred in a component. All descendants of java.awt.Component can respond to MouseEvents. This event is used for mouse events (press, release, click, enter, exit), mouse motion events (moves and drags) and the mouse-wheel event (the wheel was rotated). In general, a mouse action is considered to occur in a particular component if and only if the mouse cursor was over a visible portion of the component. Bear in mind that portions of a component may not be visible if other components such as windows or menus are obscuring them.

image from book

    java.awt.event.ComponentEvent       java.awt.event.InputEvent          java.awt.event.MouseEvent             java.awt.event.AWTEvent

image from book

Figure 13-7: MouseEvent Inheritance Hierarchy

MouseEvent inherits from ComponentEvent the convenience method getComponent(), which returns the source of the event as a Component (if it is a Component) or null otherwise. It inherits the methods getModifiers() and getWhen() from InputEvent, which are functionally equivalent to ActionEvent’s methods with the same name. It also inherits some convenience methods from InputEvent that are alternatives to using bit-operations on the result of getModifiers(). These methods are listed in tables 13-9 through 13-11.

Table 13-9: ComponentEvent Methods

Method Name and Purpose

public Component getComponent()

Returns the source of the event if the source is a Component. Otherwise it returns null.

Table 13-10: InputEvent Methods

Method Name and Purpose

public long getWhen()

Functionally equivalent to ActionEvent.getWhen().

public int getModifiers()

Functionally equivalent to ActionEvent.getModifiers().

Table 13-11: InputEvent Convenience Methods

Method Name and Purpose

public boolean isAltDown()

Returns whether or not the Alt modifier is down on this event.

public boolean isAltGraphDown()

Returns whether or not the Alt-Graph modifier is down on this event.

public boolean isControlDown()

Returns whether or not the Control modifier is down on this event.

public boolean isMetaDown()

Returns whether or not the Meta modifier is down on this event.

public boolean isShiftDown()

Returns whether or not the Shift modifier is down on this event.

MouseEvent defines several event ids as listed in table 13-12, each of which identifies a different mouse action.

Table 13-12: MouseEvent Event IDs

ID Name and Purpose

public static final int MOUSE_CLICKED = 500

This corresponds to the click of a mouse button. The click of the mouse is a higher level event than the press and release. The user doesn’t actually “click” the mouse. The press and release generate a mouse click if the press and release were at the same coordinate (the mouse didn’t move in between).

public static final int MOUSE_PRESSED = 501

This corresponds to the press of a mouse button.

public static final int MOUSE_RELEASED = 502

This corresponds to the release of a mouse button. Whether or not it is generated depends on where the mouse was initially pressed. If the mouse was pressed inside the component then a mouse release will be reported even if it was dragged and rel eased outside the component. On the other hand, it will not be reported if the mouse was initially pressed outside the component and then dragged and released inside the component.

public static final int MOUSE_MOVED = 503

This corresponds to the movement of the mouse while no mouse buttons were pressed.

public static final int MOUSE_ENTERED = 504

This indicates that the mouse cursor entered a visible portion of the component.

public static final int MOUSE_EXITED = 505

This indicates that the mouse cursor exited a visible portion of the component.

public static final int MOUSE_DRAGGED = 506

This corresponds to the movement of the mouse while a mouse button was pressed. The event will be generated even when the mouse is outside the component as long as the drag was initiated inside the component.

public static final int MOUSE_WHEEL = 507

This corresponds to the rotation of the mouse wheel.

MouseEvent defines additional methods that are meaningful specifically to mouse events. These are listed in table 13-13.

Table 13-13: MouseEvent-Specific Methods

Method Name and Purpose

public int getX()

Returns the X-coordinate of the event relative to the source component.

public int getY()

Returns the Y-coordinate of the event relative to the source component.

public Point getPoint()

Returns the X- and Y-coordinates of the event relative to the source component.

public int getClickCount()

Returns the number of mouse clicks associated with this event. The number of mouse clicks is associated with mouse presses, releases and clicks. It is zero for other event types.

public int getButton()

Returns which, if any, of the mouse buttons has been pressed or released. It will return one of the constant integer values (defined by MouseEvent): BUTTON1, BUTTON 2, BUTTON3 or NOBUTTON.

Because the MouseEvent constants BUTTON1, BUTTON2, BUTTON3 are not self explanatory, I prefer to use some SwingUtilities convenience methods for determining which mouse button is pressed. These are listed in table 13-14.

Table 13-14: SwingUtilities Helper Methods

Method Name and Purpose

static boolean isLeftMouseButton(MouseEvent)

Returns true if the mouse event specifies the left mouse button.

static boolean isMiddleMouseButton(MouseEvent)

Returns true if the mouse event specifies the middle mouse button.

static boolean isRightMouseButton(MouseEvent)

Returns true if the mouse event specifies the right mouse button.

Step 4: The Listener

In order to report all possible mouse event types we will have to implement three listener interfaces. These are MouseListener, MouseMotionListener and MouseWheelListener whose inheritance hierarchy is shown in figure 13-8. Their various method names are obviously derived from the event ids that MouseEvent defines. The event framework automatically calls the correct listener method for the particular event.

image from book

 java.util.EventListener    java.awt.event.MouseListener    java.awt.event.MouseMotionListener    java.awt.event.MouseWheelListener

image from book

Figure 13-8: MouseListener, MouseMotionListener, and MouseWheelListener Inheritance Hierarchy

MouseListener declares the five methods listed in table 13-15. A MouseEvent is passed as a parameter to each method of a MouseListener that is registered with a component using the addMouseListener() method.

Table 13-15: MouseListener Methods

Method

Associated Event ID

public void mousePressed(MouseEvent)

MouseEvent.MOUSE_PRESSED

public void mouseReleased(MouseEvent)

MouseEvent.MOUSE_RELEASED

public void mouseClicked(MouseEvent)

MouseEvent.MOUSE_CLICKED

public void mouseEntered(MouseEvent)

MouseEvent.MOUSE_ENTERED

public void mouseExited(MouseEvent)

MouseEvent.MOUSE_EXITED

MouseMotionListener declares the two methods listed in table 13-16. A MouseEvent is passed as a parameter to each method of a MouseMotionListener that is registered with a component using the addMouseMotionListener() method.

Table 13-16: MouseMotionListener Methods

Method

Associated Event ID

public void mouseMoved(MouseEvent)

MouseEvent.MOUSE_MOVED

public void mouseDragged(MouseEvent)

MouseEvent.MOUSE_DRAGGED

MouseWheelListener declares the one method listed in table 13-17. A MouseEvent is passed as a parameter to the mouseWheelMoved() method of a MouseWheelListener that is registered with a component using the addMouse-WheelListener() method.

Table 13-17: MouseWheelListener Methods

Method

Associated Event ID

public void mouseWheelMoved(MouseEvent)

MouseEvent.MOUSE_WHEEL

You may be wondering why there isn’t just one listener type to handle all mouse events. Well, MOUSE_MOVED and MOUSE_DRAGGED events were separated into the separate listener type MouseMotionListener because tracking the cursor's motion involves significantly more system overhead than tracking other mouse events. And the MOUSE_WHEEL event is handled separately by the MouseWheelListener because it was first introduced in release 1.4. Just to be complete, there is a fourth Listener named MouseInputListener that extends from both MouseListener and MouseMotionListener.

Step 5: The Design Approach: External class with fields

In example 13.4, we create the MyMouseListener class. In order for it to change the text of eventLabel, its methods will need access to MainFrame’s eventLabel. Since the listener methods are only passed a MouseEvent object, we need to provide our listener with an eventLabel reference. There are many ways we might do this. In this example, we’ll pass the label as a parameter to the MyMouseListener constructor so that it can keep a reference to it. If Main-Frame were to ever remove eventLabel or replace it with another component after the construction of this listener, the listener would be stuck with an invalid reference and would no longer function correctly. So, as we develop Main-Frame we must keep this dependency in mind. We have adopted this simple approach because we do not intend for MainFrame to ever change or remove eventLabel.

Example 13.4: chap13.MyMouseListener.java

image from book
 1     package chap13; 2 3     import java.awt.event.MouseEvent; 4     import java.awt.event.MouseListener; 5     import java.awt.event.MouseMotionListener; 6     import java.awt.event.MouseWheelEvent; 7     import java.awt.event.MouseWheelListener; 8 9     import javax.swing.JLabel; 10 11    public class MyMouseListener 12      implements MouseListener, MouseMotionListener, MouseWheelListener { 13      JLabel eventLabel; 14 15    public MyMouseListener(JLabel eventLabel) { 16      this.eventLabel = eventLabel; 17    } 18    public void mouseClicked(MouseEvent e) { 19      displayEvent(e); 20    } 21    public void mousePressed(MouseEvent e) { 22      displayEvent(e); 23    } 24    public void mouseReleased(MouseEvent e) { 25      displayEvent(e); 26    } 27    public void mouseEntered(MouseEvent e) { 28      displayEvent(e); 29    } 30    public void mouseExited(MouseEvent e) { 31      displayEvent(e); 32    } 33    public void mouseDragged(MouseEvent e) { 34      displayEvent(e); 35    } 36    public void mouseMoved(MouseEvent e) { 37      displayEvent(e); 38    } 39    public void mouseWheelMoved(MouseWheelEvent e) { 40      displayEvent(e); 41    } 42    private String getButtonName(MouseEvent e) { 43      int button = e.getButton(); 44      if (button == MouseEvent.BUTTON1) { 45        return "Button1"; 46      } else if (button == MouseEvent.BUTTON2) { 47        return "Button2"; 48      } else if (button == MouseEvent.BUTTON3) { 49        return "Button3"; 50      } else { 51        return "Unidentified Button"; 52      } 53    } 54    private void displayEvent(MouseEvent e) { 55      String s; 56      int id = e.getID(); 57      if (id == MouseEvent.MOUSE_CLICKED) { 58        s = getButtonName(e) + " mouseClicked[" + e.getClickCount() + "]"; 59      } else if (id == MouseEvent.MOUSE_PRESSED) { 60        s = getButtonName(e) + " mousePressed[" + e.getClickCount() + "]"; 61      } else if (id == MouseEvent.MOUSE_RELEASED) { 62        s = getButtonName(e) + " mouseReleased[" + e.getClickCount() + "]"; 63      } else if (id == MouseEvent.MOUSE_ENTERED) { 64        s = "mouseEntered"; 65      } else if (id == MouseEvent.MOUSE_EXITED) { 66        s = "mouseExited"; 67      } else if (id == MouseEvent.MOUSE_DRAGGED) { 68        s = "mouseDragged"; 69      } else if (id == MouseEvent.MOUSE_MOVED) { 70        s = "mouseMoved"; 71      } else if (id == MouseEvent.MOUSE_WHEEL) { 72        s = "mouseWheelMoved"; 73      } else { 74        s = "Unknown Event"; 75      } 76 77      s += " :" + "(" + e.getX() + ", " + e.getY() + ")"; 78      System.out.println(s); 79      eventLabel.setText(s); 80    } 81  }
image from book

The displayEvent() method (lines 54 - 80) prints event information to the console in addition to displaying it in eventLabel because the mouseClicked event is generated so quickly after the mouseReleased event that the label doesn’t have enough time to display the mouseReleased event.

Step 6: Registration

In ListeningMainFrame2, we register the listener with the JTextArea. Even though MyMouseListener implements all three mouse listener types, a component only reports events to a listener based on which registration methods are used. So, we must register myMouseListener three times. If after running ListeningMainFrame2 you get tired of all the mouseMoved and mouseDragged reporting, you can simply comment out line 9 which registers the listener as a MouseMotionListener.

Example 13.5: chap13.ListeningMainFrame2

image from book
 1     package chap13; 2 3     public class ListeningMainFrame2 extends ListeningMainFrame1 { 4 5       protected void addListeners() { 6         super.addListeners(); 7         MyMouseListener myMouseListener = new MyMouseListener(eventLabel); 8         textArea.addMouseListener(myMouseListener); 9         textArea.addMouseMotionListener(myMouseListener); 10        textArea.addMouseWheelListener(myMouseListener); 11      } 12      public static void main(String[] arg) { 13        ListeningMainFrame2 frame = new ListeningMainFrame2(); 14      } 15    }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

    javac –d classes -sourcepath src src/chap13/ListeningMainFrame2.java    java –cp classes chap13.ListeningMainFrame2

ListeningMainFrame3

With ListeningMainFrame3, we will investigate key events. We will also encounter our first application of an inner class.

Step 1: Application Behavior

We’ll use the event mechanism to capture keyboard activity in the text area and report it in the same JLabel used for reporting mouse events (eventLabel).

Step 2: The Source

The source component is again the JTextArea, textArea.

Step 3: The Event

We are interested in KeyEvent whose inheritance hierarchy is shown in figure 13-9. A KeyEvent is a low-level event indicating that a key was pressed, released or typed in a component. All descendants of java.awt.Component can respond to KeyEvents. In order to receive KeyEvents, a component must have the keyboard focus. Whether or not a component has keyboard focus is beyond the scope of this chapter to discuss fully. In general, text fields and text areas will have the keyboard focus when they have been clicked with a mouse or when the press of the tab key brings the keyboard focus to them. KeyEvent defines the three event ids listed in table 13-18 and the three methods listed in table 13-19.

image from book

 java.awt.AWTEvent    java.awt.event.ComponentEvent       java.awt.event.InputEvent          java.awt.event.KeyEvent

image from book

Figure 13-9: KeyEvent Inheritance Hierarchy

Table 13-18: KeyEvent Event IDs

ID Name and Purpose

public static final int KEY_TYPED = 400

This KeyEvent is reported to a component when a character is entered. The typing of a character is higher-level than key presses and releases and doesn’t generally depend on the platform or keyboard layout. It is often produced by a single key press but it can be produced by a combination of key presses (as in ‘shift’ + ‘a’). Key typed events are only generated for keys that produce Unicode characters. That excludes keys like modifier keys or arrow keys.

public static final int KEY_PRESSED = 401

This corresponds to the press of a key. This KeyEvent is generated repeatedly when a key is held down. If the key press signifies a Unicode character such as the letter ‘a’ for example then a key typed event is also generated. If, on the other hand, the key pressed was the shift-key or some other key that by itself does not signify a Unicode character, then no key typed event is generated.

public static final int KEY_RELEASED = 402

This corresponds to the release of a key. Key releases are not usually necessary to generate a key typed event, but there are some cases where the key typed event is not generated until a key is released (e.g., entering ASCII sequences via the Alt-Numpad method in Windows).

Table 13-19: KeyEvent Methods

Method Name and Purpose

public char getKeyChar()

Returns the character associated with the key in this event. The getKeyChar() method always returns a valid Unicode character or CHAR_UNDEFINED.

public int getKeyCode()

Returns the integer keyCode associated with the key in this event. For key pressed and key released events, the getKey-Code method returns the event's keyCode. This value will be one of the many constants with names like VK_XXX which are defined by KeyEvent. Calling this method is the only way to find out about keys that don't generate character input (e.g., action keys, modifier keys, etc.). For key typed events, the getKeyCode() method always returns VK_UNDEFINED.

public int getKeyLocation()

Returns the location of the key that originated a key press or key released event. This provides a way to distinguish keys that occur more than once on a keyboard, such as the two shift keys, for example. The possible values are KEY_LOCATION_STANDARD, KEY_LOCATION_LEFT, KEY_LOCATION_RIGHT, KEY_LOCATION_NUMPAD, or KEY_LOCATION_UNKNOWN. This method always returns KEY_LOCATION_UNKNOWN for key-typed events.

Step 4: The Listener

The Listener we need is KeyListener whose inheritance hierarcy is shown in figure 13-10. KeyListener declares the three methods listed in table 13-20 whose names are obviously derived from the event ids that KeyEvent defines. Every time a key event occurs on a component, a KeyEvent object is passed as a parameter to the appropriate method of every KeyListener that is registered with that component.

image from book

 java.util.EventListener    java.awt.event.KeyListener

image from book

Figure 13-10: KeyListener Inheritance Hierarchy

Table 13-20: KeyListener Methods

Method

Associated Event ID

public void keyTyped(KeyEvent)

KeyEvent.KEY_TYPED

public void keyPressed(KeyEvent)

KeyEvent.KEY_PRESSED

public void keyReleased(KeyEvent)

KeyEvent.KEY_RELEASED

Step 5: The Design Approach: Inner Class (field-level)

Just as did MyMouseListener, MyKeyListener will need access to eventLabel. Many times, an EventListener class needs access to fields or methods of an object (in our case MainFrame) that would best be left private or protected. If, as is often the case, that listener will only ever be created or referenced by that same object, then it may be preferable to declare the listener as an inner class of the object, thereby giving it automatic access to all the object’s fields and simplifying access issues such as pointed out in MyMouseListener. So, unlike MyMouseListener, MyKey-Listener will be an inner class of MainFrame and will not need any special coding to access eventLabel. Nor need it ever worry that eventLabel might ever change. It will always have a valid reference to eventLabel.

MyKeyListener will be an inner class of ListeningMainFrame3, and ListeningMainFrame3 will be its enclosing class. We will define it at the same level as methods and fields of ListeningMainFrame3, effectively making it a property of the ListeningMainFrame3 object. Consequently, it will have the same accessibility as other class fields and methods of ListeningFrame3. By declaring the MyKeyListener class to be private, its visibility will be restricted to ListeningFrame3 only. If it were declared protected, it would be visible only to ListeningMainFrame3 and its subclasses. If it were declared public or given package protection, then other external classes would be able to reference it.

An inner class is referenced by a non-enclosing class like this: “EnclosingClassName.InnerClassName”. So, if we were to declare MyKeyListener as a public inner class (we won’t), then an external class could contain statements such as

 ListeningMainFrame3 frame = new ListeningMainFrame3(); ListeningMainFrame3.MyKeyListener keyL = frame.new MyKeyListener();

If ListeningMainFrame3.MyKeyListener were a static class, then it wouldn’t be necessary to have an instance of ListeningMainFrame3 from which to create the listener instance. Code such as this could be used:

 ListeningMainFrame3.MyKeyListener keyL =                                new ListeningMainFrame3.MyKeyListener();

Within the body of an inner class, the reserved word “this” refers to the inner class instance. An inner class can refer to its enclosing class instance with the notation <EnclosingClassName>.this. As an example, MyKeyListener can refer to its enclosing ListeningMainFrame3 class instance as “ListeningMainFrame3.this”.

Event information is printed to the console, in addition to being displayed in eventLabel, because the key released event often follows so quickly after the key typed event that the label doesn’t have enough time to display the key typed event.

Step 6: Registration

Registering the key listener is handled by line 11 of example 13.6.

Example 13.6: chap13.ListeningMainFrame3.java

image from book
 1     package chap13; 2 3     import java.awt.event.KeyEvent; 4     import java.awt.event.KeyListener; 5 6     public class ListeningMainFrame3 extends ListeningMainFrame2 { 7 8       protected void addListeners() { 9         super.addListeners(); 10        KeyListener myKeyListener = new MyKeyListener(); 11        textArea.addKeyListener(myKeyListener); 12      } 13      protected class MyKeyListener implements KeyListener { 14        //implementing KeyListener 15      public void keyTyped(KeyEvent e) { 16        String display = "keyTyped: " + String.valueOf(e.getKeyChar()); 17        System.out.println(display); 18        eventLabel.setText(display); 19      } 20      public void keyPressed(KeyEvent e) { 21        String display = "keyPressed: " + String.valueOf(e.getKeyChar()); 22        System.out.println(display); 23        eventLabel.setText(display); 24      } 25      public void keyReleased(KeyEvent e) { 26        String display = "keyReleased: " + String.valueOf(e.getKeyChar()); 27        System.out.println(display); 28        eventLabel.setText(display); 29     } 30    } 31    public static void main(String[] arg) { 32      ListeningMainFrame3 frame = new ListeningMainFrame3(); 33    } 34   }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

 javac –d classes -sourcepath src src/chap13/ListeningMainFrame3.java java –cp classes chap13.ListeningMainFrame3

Incidentally, although the MyKeyListener class is internal to ListeningMainFrame3, the compiler generates a separate class file for it. Look at the generated class files and you should see a file with a name like image from book ListeningMainFrame3$MyKeyListener.class corresponding to the inner class! Its name clearly reflects its nested nature.

ListeningMainFrame4

With ListeningMainFrame4, we will employ another type of inner class.

Step 1: Application Behavior

When the user clicks on the “Choose Background Color” button we will popup a JColorChooser to allow the user to change the background color of one of MainFrame’s panels. When the user clicks the “Default Background Color” button, the background will return to its default color.

Step 2: The Source

The source components are the two JButtons, bgColorButton and defaultColorButton.

Step 3: The Event

We could of course choose the MouseEvent, but as you understand by now, it would be better to use the ActionEvent.

Step 4: The Listener

This will be the ActionListener.

Step 5: The Design Approach: Inner Class (local-variable-level)

While we’re on the subject of creating inner classes, let’s try another way to create an inner class. In the previous approach, we declared MyKeyListener at the same level as class fields and methods are declared. We can also declare a class within the body of a method at the local variable scope. A class declared this way is visible only within the body of the method, and neither an external class nor its enclosing class can reference it. The inner class can still refer to its enclosing class in the same manner as do field-level inner classes. We will create an inner class at the local variable level in lines 14 - 32 of example 13.7.

Example 13.7: chap13.ListeningMainFrame4.java

image from book
 1     package chap13; 2 3     import java.awt.Color; 4     import java.awt.event.ActionEvent; 5     import java.awt.event.ActionListener; 6 7     import javax.swing.JColorChooser; 8 9     public class ListeningMainFrame4 extends ListeningMainFrame3 { 10 11      protected void addListeners() { 12        super.addListeners(); 13 14        class ColorButtonListener implements ActionListener { 15          public void actionPerformed(ActionEvent e) { 16            Object o = e.getSource(); 17 18            if (o == bgColorButton) { 19              Color color = displayOptionsPanel.getBackground(); 20              color = 21                JColorChooser.showDialog( 22                  bgColorButton, 23                  "Choose Background Color", 24                  color); 25              if (color != null) { 26                displayOptionsPanel.setBackground(color); 27              } 28            } else if (o == defaultColorButton) { 29              displayOptionsPanel.setBackground(null); 30            } 31          } 32        }; 33 34        ColorButtonListener colorButtonListener = new ColorButtonListener(); 35        bgColorButton.addActionListener(colorButtonListener); 36        defaultColorButton.addActionListener(colorButtonListener); 37      } 38      public static void main(String[] arg) { 39        ListeningMainFrame4 frame = new ListeningMainFrame4(); 40      } 41   }
image from book

Step 6: Registration

Registration is handled by lines 35 and 36 of example 13.7.

Use the following commands to compile and execute the example: From the directory containing the src folder:

 javac –d classes -sourcepath src src/chap13/ListeningMainFrame4.java java –cp classes chap13.ListeningMainFrame4

The compiler generates a separate class file for any inner class. Look at the generated class files and you should see a file with a name like ListeningMainFrame4$1$ColorButtonListener.class corresponding to our inner class.

ListeningMainFrame5

ListeningMainFrame5 will add lots of interactivity to our application without creating any additional class at all.

Step 1: Application Behavior

This time we will add the following interactivity to our application.

  • The five radio buttons will change the border of one of MainFrame’s JPanels (displayOptionsPanel).

  • The four checkboxes will add or remove items from the JList (saladList).

  • The combo box will set the font style of the JTextArea (textArea).

  • The “Lock Text Area” button will toggle the JTextArea’s (textArea) editability.

  • The “Enter Secret Code” field will compare its text to the JTextField’s (chosenItemTextField) text and popup a message.

Step 2: The Source

The source components are the five JRadioButtons (titleBorderRadioButton, lineBorderRadioButton, etchedBorderRadioButton, bevelBorderRadioButton and noBorderRadioButton), the four JCheckBoxes (vegetablesCheckBox, fruitsCheckBox, nutsCheckBox and cheesesCheckBox), the JToggleButton (lockingToggleButton), the JComboBox (fontStyleComboBox) and the JPasswordField (secretCodeField).

Step 3: The Event

We’ll listen for ActionEvents in all cases.

Step 4: The Listener

We’ll use ActionListener again, of course.

Step 5: The Design Approach: Existing Class

Creating an inner class is certainly convenient (and we will revisit that approach again later with a twist), but if all we’re doing is implementing an interface, we don’t really need to create a new class at all. We could take any existing class and use it as a listener simply by having it implement whatever EventListener interfaces we need. In this example, we’ll have ListeningMainFrame5 implement ActionListener. Its actionPerformed method will query the ActionEvent object to find out what component generated the event.

Using an existing class is perhaps the most efficient way to approach the implementation of an event listener as there is always a bit of overhead with the loading of new class type. However, it can lead to long method definitions, and it tends to clump lots of code together that might be more elegantly expressed separately.

Step 6: Registration

Registration is handled by the addListeners method (lines 20 - 39) of example 13.8. It might seem a little strange to pass “this” as the parameter to the addActionListener() methods but it’s perfectly legitimate. Once again, the design approach has changed, but we’re still just implementing an EventListener interface and registering the implementing object (“this” in this case) with the components.

Example 13.8: chap13.ListeningMainFrame5.java

image from book
 1     package chap13; 2 3     import java.awt.Color; 4     import java.awt.Font; 5     import java.awt.event.ActionEvent; 6     import java.awt.event.ActionListener; 7     import java.util.List; 8 9     import javax.swing.JCheckBox; 10    import javax.swing.JOptionPane; 11    import javax.swing.border.BevelBorder; 12    import javax.swing.border.EtchedBorder; 13    import javax.swing.border.LineBorder; 14    import javax.swing.border.TitledBorder; 15 16    public class ListeningMainFrame5 17      extends ListeningMainFrame4 18      implements ActionListener { 19 20      protected void addListeners() { 21        super.addListeners(); 22 23        titleBorderRadioButton.addActionListener(this); 24        lineBorderRadioButton.addActionListener(this); 25        etchedBorderRadioButton.addActionListener(this); 26        bevelBorderRadioButton.addActionListener(this); 27        noBorderRadioButton.addActionListener(this); 28 29        vegetablesCheckBox.addActionListener(this); 30        fruitsCheckBox.addActionListener(this); 31        nutsCheckBox.addActionListener(this); 32        cheesesCheckBox.addActionListener(this); 33 34        lockingToggleButton.addActionListener(this); 35 36        fontStyleComboBox.addActionListener(this); 37 38        secretCodeField.addActionListener(this); 39      } 40      public void actionPerformed(ActionEvent e) { 41        Object o = e.getSource(); 42 43        //borders 44        if (o == titleBorderRadioButton) { 45          displayOptionsPanel.setBorder(new TitledBorder("Panel Options")); 46          System.out.println(displayOptionsPanel.getInsets()); 47        } else if (o == lineBorderRadioButton) { 48          displayOptionsPanel.setBorder(new LineBorder(Color.blue, 10)); 49          System.out.println(displayOptionsPanel.getInsets()); 50        } else if (o == etchedBorderRadioButton) { 51          displayOptionsPanel.setBorder(new EtchedBorder()); 52          System.out.println(displayOptionsPanel.getInsets()); 53        } else if (o == bevelBorderRadioButton) { 54          displayOptionsPanel.setBorder(new BevelBorder(BevelBorder.LOWERED)); 55          System.out.println(displayOptionsPanel.getInsets()); 56        } else if (o == noBorderRadioButton) { 57          displayOptionsPanel.setBorder(null); 58          System.out.println(displayOptionsPanel.getInsets()); 59 60          //lock/unlock the textarea 61        } else if (o == lockingToggleButton) { 62          boolean selected = lockingToggleButton.isSelected(); 63          textArea.setEditable(!selected); 64          textArea.setOpaque(!selected); 65 66          //update the list 67        } else if ( 68          o == vegetablesCheckBox 69            || o == fruitsCheckBox 70            || o == nutsCheckBox 71            || o == cheesesCheckBox) { 72          boolean selected = ((JCheckBox)o).isSelected(); 73          List items = null; 74          if (o == vegetablesCheckBox) { 75            items = vegetables; 76          } else if (o == fruitsCheckBox) { 77            items = fruits; 78          } else if (o == nutsCheckBox) { 79            items = nuts; 80          } else if (o == cheesesCheckBox) { 81            items = cheeses; 82          } 83          if (selected) { 84            saladListItems.addAll(items); 85          } else { 86            saladListItems.removeAll(items); 87          } 88          saladList.setListData(saladListItems); 89 90          //change the font style 91        } else if (o == fontStyleComboBox) { 92          int fontStyle = Font.PLAIN; 93          int i = fontStyleComboBox.getSelectedIndex(); 94          if (i == 0) { 95            fontStyle = Font.PLAIN; 96          } else if (i == 1) { 97            fontStyle = Font.BOLD; 98          } else if (i == 2) { 99            fontStyle = Font.ITALIC; 100         } else if (i == 3) { 101           fontStyle = Font.BOLD | Font.ITALIC; 102         } 103         textArea.setFont(textArea.getFont().deriveFont(fontStyle)); 104 105         //compare secretCodeField to chosenItemTextField 106       } else if (o == secretCodeField) { 107         String message; 108         int messageType; 109         if (secretCodeField 110           .getPassword() 111           .equals(chosenItemTextField.getText())) { 112           message = "You guessed the secret code!"; 113           messageType = JOptionPane.INFORMATION_MESSAGE; 114         } else { 115           message = "That was not the secret code."; 116           messageType = JOptionPane.ERROR_MESSAGE; 117         } 118         JOptionPane.showMessageDialog( 119           secretCodeField, 120           message, 121           "Results", 122           messageType); 123       } 124     } 125     public static void main(String[] arg) { 126       ListeningMainFrame5 frame = new ListeningMainFrame5(); 127     } 128   }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

 javac –d classes -sourcepath src src/chap13/ListeningMainFrame5.java java –cp classes chap13.ListeningMainFrame5

ListeningMainFrame6

With ListeningMainFrame6, we will investigate an event type that does not extend from AWTEvent.

Step 1: Application Behavior

The slider in the font panel will set the font size for text in the JTextArea (textArea).

Step 2: The Source

This time the source component is the JSlider fontSizeSlider.

Step 3: The Event

ChangeEvent is the event of choice. It is definitely a semantic event and is the first event we have dealt with that doesn’t extend from AWTEvent. ChangeEvent extends directly from EventObject (as shown in figure 13-11) and defines no methods or fields of its own. It is expected that you would query the object returned by its getSource() method or have some other means for discerning what the change is. Components that respond to ChangeEvents define for themselves what signifies a “change”. In the case of the JSlider, moving the knob generates a ChangeEvent. JComponents that respond to ChangeEvents include JSlider, JProgressBar, JSpinner, JTabbedPane, JViewport and AbstractButton.

image from book

 java.util.EventObject    javax.swing.event.ChangeEvent

image from book

Figure 13-11: ChangeEvent Inheritance Hierarchy

Step 4: The Listener

The associated listener is ChangeListener whose inheritance hierarchy is shown in figure 13-12. ChangeListener defines one method (listed in table 13-21) which is called whenever a change event occurs.

image from book

 java.util.EventListener    javax.swing.event.ChangeListener

image from book

Figure 13-12: ChangeListener Inheritance Hierarchy

Table 13-21: ChangeListener Methods

Method Name and Purpose

public void stateChanged(ChangeEvent)

Called when the target of the listener has changed its “state”.

Step 5: The Design Approach: Anonymous Class

An anonymous class is a class that cannot be referred to – not because of visibility issues but because it is never assigned a name. This is often an attractive approach when it comes to creating listeners. In this example we will implement ChangeListener in an anonymous class.Notice how ListeningMainFrame6 implements the ChangeListener interface without ever assigning a class name to it. The variable myChangeListener is an instance of a new class whose entire reason for existence is to be assigned to the myChangeListener instance variable.

Step 6: Registration

Registration is handled by line 23 of example 13.9.

Example 13.9: chap13.ListeningMainFrame6.java

image from book
 1     package chap13; 2 3     import java.awt.Font; 4 5     import javax.swing.JSlider; 6     import javax.swing.event.ChangeEvent; 7     import javax.swing.event.ChangeListener; 8 9     public class ListeningMainFrame6 extends ListeningMainFrame5 { 10 11      protected void addListeners() { 12        super.addListeners(); 13        ChangeListener myChangeListener = new ChangeListener() { 14          public void stateChanged(ChangeEvent e) { 15            JSlider slider = (JSlider)e.getSource(); 16            int value = slider.getValue(); 17            sliderLabel.setText(String.valueOf(value)); 18            Font font = textArea.getFont(); 19            textArea.setFont(font.deriveFont((float)value)); 20          } 21        }; 22 23         fontSizeSlider.addChangeListener(myChangeListener); 24      } 25      public static void main(String[] arg) { 26        ListeningMainFrame6 frame = new ListeningMainFrame6(); 27      } 28    }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

 javac –d classes -sourcepath src src/chap13/ListeningMainFrame6.java java –cp classes chap13.ListeningMainFrame6

Even though our listener class is “anonymous”, the compiler gives it a name when it generates the class file for ListeningMainFrame6. If you’re curious, look at the generated class files and you will see a file named something like image from book ListeningMainFrame6$1.class.

ListeningMainFrame7

ListeningMainFrame7 will introduce another event that doesn’t extend from AWTEvent. It will also employ the most syntactically concise design approach.

Step 1: Application Behavior

When the user selects an item in the “Salad Options” list, that item’s name will become the text for the JTextField (chosenItemField).

Step 2: The Source

The source component is the JList saladList.

Step 3: The Event

ListSelectionEvent is the event of choice. Its inheritance hierarchy is shown in figure 13-13. This semantic event is generated when there has been a change in the current selection of a JTable or JList. When it is generated, the selection state may have changed for one or more rows in the list or table. Similarly to ChangeEvent, you have to query the source to obtain more information than the ListSelectionEvent instance contains. ListSelectionEvent defines the three methods listed in table 13-22.

image from book

 java.util.EventObject    javax.swing.event.ListSelectionEvent

image from book

Figure 13-13: ListSelectionEvent Inheritance Hierarchy

Table 13-22: ListSelectionEvent Methods

Method Name and Purpose

public int getFirstIndex()

Returns the index of the first row whose selection may have changed.

public int getLastIndex()

Returns the index of the last row whose selection may have changed.

public boolean getValueIsAdjusting()

Returns true if this is one of multiple change events.

Step 4: The Listener

The corresponding listener is the ListSelectionListener whose inheritance hierarchy is shown in figure 13-14.

image from book

 java.util.EventListener    javax.swing.event.ListSelectionListener

image from book

Figure 13-14: ListSelectionListener Inheritance Hierarchy

ListSelectionListener defines one method which is called whenever a ListSelectionEvent occurs. This method is listed in table 13-23.

Table 13-23: ListSelectionListener Methods

Method Name and Purpose

public void valueChanged(ListSelectionEvent e)

Called whenever the value of the selection changes.

Step 5: The Design Approach: Anonymous Class and Instance

Finally, in our exploration of design approaches, we will create an anonymous listener class whose instance is also anonymous. We don’t need to assign a name to an instance variable if we’re only going to refer to it long enough to pass it once as a parameter. So, in this last design approach we create the listener class and its instance both anonymously. This is a wonderfully concise and frequently appropriate design approach.

Step 6: Registration

Registration is handled in place by line 10 of example 13.10. The use of anonymous inner classes can be helpful once you get used to the syntax because the listener instance is defined in the very same place as it is used.

Example 13.10: chap13.ListeningMainFrame7.java

image from book
 1     package chap13; 2 3     import javax.swing.event.ListSelectionEvent; 4     import javax.swing.event.ListSelectionListener; 5 6     public class ListeningMainFrame7 extends ListeningMainFrame6 { 7 8       protected void addListeners() { 9         super.addListeners(); 10        saladList.addListSelectionListener(new ListSelectionListener() { 11          public void valueChanged(ListSelectionEvent e) { 12            chosenItemTextField.setText((String)saladList.getSelectedValue()); 13          } 14        }); 15      } 16      public static void main(String[] arg) { 17        ListeningMainFrame7 frame = new ListeningMainFrame7(); 18      } 19    }
image from book

Use the following commands to compile and execute the example. From the directory containing the src folder:

 javac –d classes -sourcepath src src/chap13/ListeningMainFrame7.java java –cp classes chap13.ListeningMainFrame7




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net