Chapter 17: GUI Events

17.1: Java 1.0 Event

Class java.awt.Event object can represent all Java events. It has a field id which indicates the type of the event - a mouse down/up, ASCII key pressed/up, Non-ASCII key (e.g. F1, Delete key) pressed/up, a component lost/got focus, etc. For any of these events, it has a static enum. Therefore, to find out exactly what happens in your event handling method, you may have to use cascaded if statements such as

   
   if(e1.id == Event.KEY_PRESS) 

to find out what happens. This is a non-OO approach.

When an ASCII key is pressed, Event . id has a value of KEY_PRESS as indication, and field key holds the unicode value of that key (e.g., for 'a' it is 97, 'b' 98, etc.). For the non-ASCII keys, key will hold corresponding static constants representing their value (e.g., Event.F1 for F1 key).

The Java toolkit creates all events. Ultimately, the OS is responsible for reporting events coming from the keyboard, mouse etc. These events are then passed to the Toolkit, which determines which component the event belongs to (based on current focus, location of event etc.).

The old and the new event models are completely separate. All events in the old model are passed to handleEvent, but if a component is using the new model, handleEvent is not used at all. The way AWT finds out what model a component is using is: only when a component has registered the corresponding listeners, or have explicitly enabled the events using enableEvents, will it be regarded as using new event model, and the corresponding events be sent to it. If none of this happens, the component is regarded as using old event model.

17.2: Component's Event Handling Methods

* handleEvent( )

Java 1.0 method. Already deprecated. When an event happens, an Event object is created, and the handleEvent method of the Component on focus is called and the Event object is passed to it. You can then implement this method and do whatever you like to handle the event. When you have more than one possible events, you have to use cascaded if statements in handleEvent.

* Methods to handle some standard old-style events

Among all old-style events, there are a small groups of "standard" events related to buttons, checkboxes, drop-down lists, menus, key down, got/lost focus, mouse down/up/move/drag/enter/exit, etc. When this events are passed to handleEvent, it will automatically call the corresponding methods such as

action(Event, Object)

keyDown(Event, int key)

keyUp(Event, int key)

lostFocus(Event, Object)

gotFocus(Event, Object)

mouseDown(Event, int x, int y)

mouseUp(Event, int x, int y)

mouseMove(Event, int x, int y)

mouseDrag(Event, int x, int y)

mouseEnter(Event, int x, int y)

mouseExit(Event, int x, int y)

If you only want to handle some of these standard events, you needn't implement handleEvent. You only need to implement these standard event handling methods.

Especially, method action will be called by handleEvent when events of those standard components such as buttons, checkboxes, drop-down lists, menus happen.

All these methods have a return type as boolean, which indicates whether the passed event has been handled. Among all the above methods, only handleEvent and action is designed for handling more than one kinds of events, so in these two methods a cascaded if structure is needed to check what kind of event it is. Suppose in a handleEvent or an action you are only interested in one or several kinds of events, then at the last else branch you should call the super class's handleEvent or action to let it handle the unhandled event. For the rest of the methods, you can always return true, because they are only called by handleEvent when the exact type of event is passed to handleEvent.

17.3: Java 1.1 Events

Old event model has only one Event class to represent all events. To find out what kind of event (standard component event from Buttons, TextFields, Lists, etc., or keyboard event, mouse event etc.) in your event handling code, you have to use cascaded if statements. This is a non-OO approach.

Java 1.1 provides a set of events in package java.awt.event representing different types of events.

As said before, if you do not do anything to a component, this component will be treated by AWT as using old event model, and all events will be sent to its handleEvent method. To enable new event model for a component, you have to either explicitly or implicitly call method enableEvent. When you register one type of event listener to a component by calling add___Listener, it will implicitly call enableEvent with the corresponding event mask.

Only the event with registered listener or enabled events will be sent to the component.

New Java divide all events into several categories:

Event

Event Description

Event Originators

EventListener Methods

ActionEvent

a component-defined action occurred. This high-level event is generated by a component (such as a Button) when the component-specific action occurs (such as being pressed).

Button, List, TextField, MenuItem, and its derivatives including CheckboxMenuItem, Menu, and PopupMenu

actionPerformed

Adjustment Event

The adjustment event emitted by Adjustable objects.

Scrollbar or anything you create that implements the Adjustable interface

adjustmentValueChanged

ChangeEvent

The thumb on the slider is moved.

JSlider

stateChanged

Component Event

A low-level event indicating that a component moved, changed size, or changed visibility (also, the root class for the other component-level events).

Component events are provided for notification purposes ONLY; The AWT will automatically handle component moves and resizes internally

Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField

componentHidden
componentShown
componentMoved
componentResized

Container Event

A low-level event indicating that a container's component was added or removed. For notification purposes ONLY

Container and its derivatives, including Panel, Applet, ScrollPane, Window, Dialog, FileDialog, and Frame

componentAdded
componentRemoved

FocusEvent

A component has gained or lost the keyboard focus. Generated by a component (such as a text field).

Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame Label, List, Scrollbar, TextArea, and TextField

focusGained
focusLost

ItemEvent

An item was selected or deselected. Generated by an ItemSelectable object (such as a List) when an item is selected or de-selected.

Checkbox, CheckboxMenuItem, Choice, List, and anything that implements the ItemSelectable interface

itemStateChanged

KeyEvent

A keystroke occurred in a component. Generated by a component object (such as a text field) when a key is pressed, released, or typed.

Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField

keyPressed
keyReleased
keyTyped

ListSelectionEvent

An item in a JList has been selected.

JList

(ListSelectionListener)

valueChanged

MouseEvent

A mouse action occurred in a component. Used both for mouse events (click, enter, exit) and mouse motion events (moves and drags).

Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField

(MouseListener)

mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased

Dito

Dito

Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField

(MouseMotion Listener)

mouseDragged
mouseMoved

TextEvent

A semantic event which indicates that an object's text changed. Generated by an object (such as a TextComponent) when its text changes.

Anything derived from TextComponent, including TextArea and TextField

textValueChanged

Window Event

A low-level event which indicates that a window has changed its status. Generated by a Window object when it is opened, closed, about to close, activated or deactivated, iconified or deconified.

Window and its derivatives, including Dialog, FileDialog, and Frame

windowOpened
windowClosing
windowClosed
windowActivated
windowDeactivated
windowIconified
windowDeiconified

The follow table lists all the events that a component can fire:

Component

Events supported by this component

Adjustable

AdjustmentEvent

Applet

ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Button

ActionEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Canvas

FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Checkbox

ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

CheckboxMenuItem

ActionEvent, ItemEvent

Choice

ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Component

FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Container

ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Dialog

ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

FileDialog

ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Frame

ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

JList

ListSelectionEvent

JSlider

ChangeEvent

Label

FocusEvent, KeyEvent, MouseEvent, ComponentEvent

List

ActionEvent, FocusEvent, KeyEvent, MouseEvent, ItemEvent, ComponentEvent

Menu

ActionEvent

MenuItem

ActionEvent

Panel

ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

PopupMenu

ActionEvent

Scrollbar

AdjustmentEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

ScrollPane

ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

TextArea

TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

TextComponent

TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

TextField

ActionEvent (when enter is pressed), TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

Window

ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

For each type of event, there is a corresponding listener interface with relevant methods to handle this specific event. Events are "fired" by every kind of components (keyboard events are fired by OS through Java toolkit).

When you use a component, you must call that component's relevant add___Listener method to register the objects to which you want to deliver the fired event, so that they can receive this event and handle it. Fore example, if you expect a component to fire TextEvent, you should call its addTextListener to register an TextListener object.

For all objects who want to be notified of a certain type of event, they must implement the corresponding Listener interface. For example, if an object want to listen to TextEvent, it must implement interface TextListener.

* Event-self-handled Component

A listener can either be an independent class with other functionality, who would simply like to be notified when something happened far away, or a class specially designed to handle this event. The later one can be more precisely called an event handler. It is a good idea to make an event handler an inner class inside the event originator. It is not only because logically it's better to group the event handler together with the originator, but also because the event handler may very probably need to access the data members of the originator, and an inner class can directly access them. Such an event originator can be called a self-contained event originator. It includes all event handling inner classes.

Normally, for each event originator object, even if they are of the same type and fires the same type of events, e.g., two buttons "OK" and "Exit", they should still have their own separate event handlers - after all, that's why we make two buttons instead of one. But it is not always necessary to do that. If you one event handler can gracefully handle all events, you can have one.

All event objects inherits from EventObject. It keeps a reference to the object who fires it. It has a getSource method to return the event originator.

17.4: Event Distributing Process under New Event Model

Under new event model, all events are first sent to the component's processEvent method. This method will identify the type of the event, and send it. Normally the whole process of event distribution happens internally and implicitly. What programmers need to do is to simply provide event handlers to receive events at the end of the route. However, if we want to do some thing before events are classified into specific types, we have to know the distribution process. The event distributing process has two steps:

* Step 1

Any enabled event which happens over a component (not over the components it holds) will cause the processEvent method of that component be called. This is the mere entry point of an event into the distributing process. By default processEvent will identify the specific type of the event and call the corresponding process___Event method.

For example, if a ComponentEvent happens, processEvent will be called and it will call processComponentEvent.

There are the following process___Event methods in Component:

processComponentEvent (java.awt.event.ComponentEvent),

processFocusEvent (java.awt.event.FocusEvent), processKeyEvent(java.awt.event.KeyEvent),

processMouseEvent (java.awt.event.MouseEvent),

processMouseMotionEvent (java.awt.event.MouseEvent),

processInputMethodEvent (java.awt.event.InputMethodEvent),

processHierarchyEvent (java.awt.event.HierarchyEvent)

For Component's subclasses, they may have more such methods, e.g., Window has an extra processWindowEvent method.

* Step 2

The process___Event method will then by default deliver the Event to all registered Listeners of that type. For example, processFocusEvent will deliver the FocusEvent to FocusListeners.

* Override the event distributing process

As said before, the whole event distribution process happens internally and implicitly, and programmers only need to provide event handlers to receive events at the end of the route. This way you seldom need to make use of the passed event object, because this listener is often designed for a specific component such as a button, the listener already knows exactly what happens. It is only concerned with "what to do", not "what happened" (of course for KeyEvents you may still have to use getKeyCode to check which key is pressed, but you do not need cascading if). This is more OO, and easy to write, reuse and debug.

When you want to change the event distribution process, or attach more functionalities to it, then you have to override method processEvent and/or precess___Event. If you decide not to use event listeners to handle events, instead you want to handle thoes events in processEvent or precess___Event, you have to explicitly enable events using enableEvent. This way you often have to use the non-OO cascading if structure, the event handling code is more coupled with the other programs, so it is difficult to write, debug and reuse, and you have to call super.process___Event - this reminds us of the old event model.

   
   import java.awt.*;
   import java.awt.event.*;
   
   class Frame1 extends Frame {
       Button b1 = new Button("Button");
       public Frame1()
       {
           add(b1);
           addWindowListener(new WindowClosingHandler());
           // This enabled WindowEvent
           enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
           // This enabled KeyEvent and FocusEvent
           setSize(200,200);
           show();
       }
   
       // Level 0 event handling
   
       public void processEvent(AWTEvent e1)
       {
           System.out.print("Event -- ");
           super.processEvent(e1);
       }
   
       // Level 1 event handling
   
       public void processWindowEvent(WindowEvent e1)
       {
           System.out.println("WindowEvent!");
           super.processWindowEvent(e1);
       }
   
       public void processKeyEvent(KeyEvent e1)
       {
           System.out.println("Pressed: " + KeyEvent.getKeyText(e1.getKeyCode()));
           super.processKeyEvent(e1);
       }
   
       public void processFocusEvent(FocusEvent e1)
       {
           System.out.println(e1.paramString());
           super.processFocusEvent(e1);
       }
   
       // Level 3 event handling
   
       class WindowClosingHandler extends WindowAdapter {
           public void windowClosing(WindowEvent e1)
           {   System.exit(0);  }
       }
   }
   
   public class Test {
       public static void main(String [] args)
       {   Frame1 f1 = new Frame1();  }
   }

17.5: Example of New Event model

The following InputFilename.java is a class designed for our assignment. It is a dialog used to prompt user to input a filename.

   
   import java.awt.event.*;
   import java.awt.*;
   
   class InputFilename extends Dialog {
       TextField t1 = new TextField("Test", 15);
       Choice c1 = new Choice();
       Button b1 = new Button("  OK  ");
       Button b2 = new Button("Cancel");
       Label l1 = new Label("Please input filename...");
       Label l2 = new Label("File type...");
   
       String filename = "";
       String [] extension = {".txt", ""};
   
       public InputFilename(Frame parent)
       {
           super(parent, "Input Filename", true);
           initialise();
       }
   
       public InputFilename(Dialog parent)
       {
           super(parent, "Input Filename", true);
           initialise();
       }
   
       public void initialise()
       {
           addWindowListener(new WindowAdapter()
           {
               public void windowClosing(WindowEvent e1)
               {  dispose(); }
           });
           b1.addActionListener(new OKListener());
           b2.addActionListener(new CancelListener());
   
           c1.add("Text");
           c1.add("All types");
           c1.select("Text");
           setLayout(new FlowLayout());
           add(l1);
           add(t1);
           add(l2);
           add(c1);
           add(b1);
           add(b2);
           setSize(200,150);
           setResizable(false);
           show();
       }
   
       public String getFilename()
       {  return filename; }
   
       class OKListener implements ActionListener {
           public void actionPerformed(ActionEvent e1)
           {
               if(!t1.getText().equals(""))
               {
                   filename = t1.getText() + extension[c1.getSelectedIndex()];
                   dispose();
               }
           }
       }
   
       class CancelListener implements ActionListener {
           public void actionPerformed(ActionEvent e1)
           {  dispose(); }
       }
   }
   
   public class Test{
       public static void main(String [] argu)
       {
           Frame f1 = new Frame();
           InputFilename n1 = new InputFilename(f1);
           System.out.println("Filename is:\n" + n1.getFilename());
       }
   }

17.6: Consume an InputEvent

Class java.awt.event.InputEvent is the root event class for all component-level input events. Input events are delivered to listeners before they are processed normally by the source where they originate. E.g., when you type into a TextField, the KeyEvent is delivered to a registered KeyListener before the TextField actually displays the typed character.

This allows listeners and component subclasses to "consume" the event so that the source will not process them in their default manner. For example, if a KeyListener has its own way to handle the key press and do not want the TextField to directly display the typed character (e.g. when handling password input), it can call the consume method of the passed event object. Then when the event handling completes and control returns to the event originator - the TextField, it will not display that character as usual. If consume is not called, it will display the character in spite of what the listener has done.

17.7: Observer & Observable

All events we've been discussing before are standard Java events - they are generated automatically by the OS or JVM and dispatched to the processEvent method of the current component. But what if we want a group listeners to be notified when some thing happens in a piece of code which does not generate any standard Java event? Java provides this Observer - Observable pattern in package java.util to solve this problem.

One common application is when you have a data model and several views of this model. Once the model is changed, you hope the views will automatically update themselves. This "model-view" problem can be solved with this pattern.

* Observable

Class java.util.Obserable which represents the data model. It has an addObserver method to register Observers to itself. If any one wants to notify the registered Observers of the Observable class with an Object carrying nessary information, he should call its notifyObservers method with that object as argument.

Used to register all the view i.e. Observer objects which should be notified when this Observable objects is considered "changed".

Set the status of the Observable object to "changed". Note that this method is protected, so it is not accessible by outside clients. When something of the Observable is changed that all Observers should know, setChanged( ) method should be called.

Check the status of this Observable object.

Set the status of this Observable object to "unchanged".

This method is called to notify all registered Observers to update themselves. It will first check if hasChanged( ) returns true. If not, nothing will be done. Then this method will call all each Observer's update(Observable o, Object argu) method, passing itself and the "argu" object as arguments.

After Observer's update( ) is finished, control will go back to notifyObservers( ). Then clearChanged( ) will be called to set the status of the object back to "unchanged".

This method can either be called by outside clients when they think necessary to update the observers, or more automatically be called by the method which make essential changes on this Observable object, after it has called setChanged( ).

Therefore, if you want your class to be able to notify a list of observers, you should let it extend Observable, and at the point where a change to the object is made which is considered to be essential, call the setChanged( ) method of underlying Observable class, and the notifyObservers( ) method - if you want the notifying process to start automatically every time the Observable is changed.

* Observer

Observer is an interface with only one method:

public void update(Observable o1, Object argu)

If you want your object to be notified when another object somewhere else is changed, you should let your object implement Observer interface and its update( ) method, doing in this method whatever you think is necessary as a response to the change. The client of your object should also register your Observer object to the Observable object.

The arguments of the update( ) method are:

Observable o1: the one who notify you.

Object argu: the extra object passed by the caller of the notifier's notifyObservers( ).

   
   import java.util.*;
   
   class DataModel extends Observable {
      private String name;
   
      public DataModel(String s)
      {  name = s; }
   
      public void change()
      {
         super.setChanged();
         super.notifyObservers(name);
      }
   }
   
   class Windows implements Observer {
      private String name;
   
      public Windows(String s)
      {  name = s; }
   
      public void update(Observable o1, Object argu)
      {
         System.out.println("\n" + name + " get notified by \"" + argu +
                            "\" of " + o1.getClass().toString());
      }
   }
   
   public class Test {
      public static void main(String[] argu)
      {
         DataModel model = new DataModel("Model");
         Windows window1 = new Windows("Window 1");
         Windows window2 = new Windows("Window 2");
   
         model.addObserver(window1);
         model.addObserver(window2);

System.out.println("\"Model\" has " + model.countObservers() + "

                            observers!\n");
   
         System.out.println("Has \"Model\" been changed? "+ model.hasChanged()+
                            "\n");
   
         System.out.println("Now changing \"Model\"...");
         model.change();
   
         System.out.println("\nHas \"Model\" been changed? " + model.hasChanged() + 
                            "\n");
      }
   }

Output will be:

   
   "Model" has 2 observers!
   Has "Model" been changed? false
   Now changing "Model"...
   Window 2 get notified by "Model" of class DataModel
   Window 1 get notified by "Model" of class DataModel
   Has "Model" been changed? false