Applets and Event-Driven Programming
Event-driven programs sit and wait for events to happen. Applets normally sit around after initialization and wait.

What is distressing for us as non-GUI interface programmers is that the meaning of "programming" has changed. It is no longer the case that we write everything. Now we are reduced to writing the code that responds to events along with the code that decides what events to respond to.

Hence we have to trust that all the other glue will be there when we need it.

Applets must

Browser Control of Applets: When a browser, analysing a web page, encounters an <APPLET> tag it looks for the class file mentioned in the tag. This is loaded accross the network and into the browser.

Next the Applet object is instantiated and init() is invoked. This means the browser must do something like execute new myApplet() so the default constructor must exist.

Next the start() method of the Applet is invoked and paint() as well so that we can actually see the Applet.

The browser is in control. If a mouseClick event occurs it is the browser that finds out first and informs the Applet. After all, the Applet is occupying space on the screen reserved for the browser image isn't it?

It is the browser that calls the event listener methods to respond to events.

The browser is busy so to handle all the things going on for an Applet it sets up a separate control thread for each Applet.

Applet Life Cycle
Applets have a few methods which are invoked at milestones in the life on an Applet. These methods are written by the Applet developer but invoked by the browser managing the Applet.
  • init(): Initializes a new Applet. Should not take very long. Longer initializations should be put into a separate thread.
  • start(): Called after init() and when the Applet is ready to run. Should finish quickly and use the same thread-creation strategy as init() for longer running jobs. Called more frequently than init(). In addition to being called at startup, it also gets invoked when the Applet is resized or when the user comes back to the web-page containing the Applet after leaving it. start() and paint() always go together.
  • stop(): Called to stop an Applet. this usually means we are stopping the execution of threads started by start() if they are not dead already. My be invoked when the web-page containing the Applet is resized or iconified or when the user leaves the web-page.
  • destroy(): Called to perform final cleanup before an Applet is unloaded. Unloading happens before an Applet is reloaded and before a browser terminates.
The sequence in which these methods are invoked is that of the above figure.

An Applet can go between start() and stop() many times before it is finished.

An Applet can define none, some or all of the above methods. Click only defines init().

When you write a normal program you can follow the execution path in your mind because every bit of executed code (except library funtion calls) is written by you. Event-driven programs are written completely differently.

Be sure that your event-response methods are short and sweet. Otherwise you will see slow response times and thing hte program sluggish.

Things that take time like a long calculation or drawing a complicated picture should be done in a separate thread.

The original Click program can be improved. Instead of just responding to a mouseClick event we can break the event in two - mousePressed and mouseReleased - and respond to both of these.

    ////// CLick2.java //////
    import java .awt.Graphics;
    import java.awt.event;

    public class Click2 extends Applet {
       public void init() {
          addMouseListener(new MouseHandler(this));  // (1)
          showStatus("Click2: inititalized");
       }

       public void start() (
         if ( msg == null) msg = "  Counting Ups and Downs":
         showStatus("Click2: started");
       }

       public void stop() (
         msg = "  See you later!":
         showStatus("Click2: stopped");
       }


       public void doUp() {
         msg = "    Ups = " + ++u + "   and Downs = " + d ;
         repaint();
       }

       public void doDown() {
         msg = "    Ups = " + u + "   and Downs = " + ++d
         repaint();
       }

       public void paint(Graphics g) {
         g.drawRect(0,0,getSize().width-1,getSize().height-1);
         g.drawString(msg,5,30);  // draw at location
       }

       private int u = 0,d = 0;
       private String msg = null;
    }

    class MouseHandler extends MouseAdapter {
      MouseHandler(Click2 ap) { app = ap; }
      public void mousePressed(MouseEvent e) { app.doDown(); }  // overriding
      public void mouseReleased(MouseEvent e) { app.doUp(); }  // overriding

      private Click2 app;
    }
 
How Applets Draw
An Applet runs in the context of a browser which supplies sindow/mouse oriented GUI. An Applet is assigned an area on a browser page. The height and width are assigned in the APPLET tag.

If an Applet needs GUI components - Buttons, TextBoxes, etc - these are provided by the Java AWT. Window objects are collectively called widgets.

The AWT gives you a single development API regardless of where the Applet is going to run - X/Motif, Windows or MacOS.

Part of the AWT object hierarchy is:

Not all Components are Containers, etc. Other GUI objects like Buttons fall elsewhere under the Component hierarchy.

AWT controls the dwaring of all graphical objects. There are three member methods that draw an applet.

  • paint(): This is a Component member method. It draws the entire component correctly. Components such as Buttons have a paint() method that does the right thing. Applets need to have paint() overridden. The signature is
             public void paint(Graphics g)
             
  • repaint(): This puts out a request to redraw the entire component. This request is fulfilled as soon as is convenient. In other words, repaint() schedules the redrawing of a component but does not actually force it to happen immediately. repaint() is a Component member method but we usually only use the Applet repaint() method and redraw the entire Applet. There are several versions of repaint().
    1. repaint(): Redraw the entire component.
    2. repaint(long tm): Redraw the entire component within tm milliseconds.
    3. repaint(int x, int y, int w, int h): Redraw a specified rectangle within the component.
    4. repaint(int x, int y, int z, int w, long tm): Redraw a specified rectangle within the component within tm milliseconds.
  • update(): The AWT calls
              public void update(Graphics g)
              
    in response to a request to repaint(). The update() method is called with a graphics context object that defines the graphics environment. This object knows things like the current foreground colour, etc. update() works by clearing the component display area and then invoking paint().

An Applet is a custom component so it needs to provide its own paint() method. The paint() method draws the display area of the Applet.

Graphical components are normally "contained" in something referred to as a parent component. The container component is the component that is aware of the location anddimensions of anything it contains. Hence it is within the parent component code that we can locate and resize a contained component.

An Applet's parent is a part of the browser.

In any component, teh upper left-hand corner has coordinatees (0,0).

The current size of the component is determined by calling the two component methods

    getSize().width     // component width in pixels
    getSize().height    // component height in pixels
paint() is requested automatically after start(). After that, it is requested, via update() by calling repaint().

Although we call paint() to draw something. Even so, it is the Graphics object that we pass to paint() that does the actual drawing. Graphics objects

  • Store the graphics context as part of its state.
  • Provides access methods (get and set) to the graphical context.
  • Provides drawing methods like drawString, drawArc, drawLine, drawRect, draw3DRect, etc.
Coordinates in computer graphics are pixel counts. (0,0) is the upper left-hand corner of the drawing surface. X increases to the right and Y down the screen.
Event Handling:
Programs indicate the events they want to handle (registering event listeners) and how to handle them (writing call-back functions).

AWT components handle events in a standard way. Since an Applet is a Panel, which is an AWT component, an Applet can handle certain standard events. Not all types of components can handle all types of events.

When AWT gets an event it:

  1. Determines which component is to receive the event. We call this component the event source. If an Applet and a button both are listening for a mouseClicked event then they are both "receivers" of the event.
  2. AWT creates an AWTEvent object (subclass of java.util.EventObject).
  3. The event is reported to the source components that received the event. This is done by invoking the source processEvent() method. Only components that are listening are candidates as sources.
  4. The event source processEvent() method "fires the event" (queues the response). It calls an appropriate event-handling callback method that belongs to the listener.
Events are arranged in an event hierarchy, delivered (or fired) by event sources and handled by event listeners. The same object can be both a source and a client. In our example the Applet was both a source and a client.
  • Event Hierarchy: The java.util.EventObject class is a superclass of all event types - KeyEvent, MouseEent, ActionEvent, etc (all found in the java.awt.event package). Each of these is a subclass of java.awt.AWTEvent. You can define your own events to subclasses of java.util.EventObject or java.awt.AWTEvent.
  • Event Sources: An event source is an object that originates or delivers and event. AWT components are typical event sources. Events can be "component-level" or "semantic". mousePressed is a component-level event. buttonClicked is a semantic event. Event listeners must be registered with a source in order to receive a particular event type.
  • Event Listeners: An event listener is any object that implements an EventListener interface or extends an adapter object which extends the same interface. In our example we saw a MouseAdapter object being extended by our listener object we called MouseHandler. Adapters provide default implementations of listener interfaces.
Your application must build its own listeners and register them with sources.

It is possible to put the listener code inside the source so you end up with fewer classes but they are more cluttered.

Just what sorts of event types exist? The following are component events.

  • ComponentEvent: Component resized, moved, ...
  • FocusEvent: Component got focus, lost focus.
  • InputEvent: Any input event.
  • KeyEvent: Component got keyPressed, keyReleased, ..
  • MouseEvent: Component go mouseDown, etc, ..
  • ContainerEvent: Container added or removed a child
  • WindowEvent: Top-level window status changed.
The following are semantic events.
  • ActionEvent: An action is requested
  • AdjustmentEvent: Value has been adjusted (ScrollBar)
  • ItemEvent: Item state is changed (CheckBox)
  • TextEvent: Text object value changed (TextBox).
AWT Event Listeners:
To implement event listeners we must first create the listening objects and then register them with the event source. They will do the listening for the source.

The evetn source responds to an event by calling a call-back function defined as part of the listener. The programmer gets specific event behaviour by overriding the call-back functions.

To register a listener we use the addXyzListener() method that is part of the Component class. We pass the listener object as an argument. We do this at set-up time (in init() of the Applet, for example). The idea behind registration is for the Applet to ignore events that are not of interest.

It is also possible to removeXyzLister().

Listeners can extend adapters or implement event listener interfaces. There is typically one call-back method for each event type in the event class.

Whenever a call-back method is invoked it is supplied with ( "sent") an EventObject by the invoking environment.

For example, the FocusListener has two call-backs.

 void focusGained(FocusEvent)
      void focusLost(FocusEvent)
          
To handle events by implementing a listener interface you need to code
 class ActionHandler implements ActionListener {
     ActionHandler(ClientClass cl) {
       client = cl; // save local reference to client
     }

     private ClientClass client;

     public void actionPerformed(ActionEvent e) {
       // action handling code here such as
       //    clinet.doAction();
     }
  }                         
actionPerformed is the call-back method. This is the method invoked (scheduled) by the event source.
AWT Event Sources:
Event sources add and remove listeners as we have already seen.
   addEventTypeListener(EventTypeListener el)
      removeEventTypeListener(EventTypeListener el)
   
AWT event sources support event multicasting; ie, sending an event to multiple sources.

The AWT Component class supports registration of all component-level event listeners:

      addComponentListener(ComponentListener )
      addFocusListener(FocusListener )
      addKeyListener(keyListener )
      addMouseListener(MouseListener )
      addMouseMotionListener(MouseMotionListener )
     
High-level components such as a Button or List call also fire semantic events by invoking
   actionPerformed
     itemStateChanged
 
found in the ActionListener and ItemListener spec.

NOTE: AWT makes no promises on the order in which events are delivered to as set of registered listeners. It is safe for a listener to modify the data state of an event object without interfering with other listeners receiving the same event (must by either sequentially executed or monitored).

To make writing listeners easier there are skeletal implementations in known adapter classes

  ComponentAdapter
    FocusAdapter
    KeyAdapter
    MouseAdapter
    MouseMotionAdapter
    WindowAdapter
                   
All these adapters implement corresponding listeners by implementing "do-nothing" methods. You can subclass an adapter and implement just the methods you need. All the other methods are invoked when the events that don't interest you occur but since they "do nothing" it appears as though the event was not responded to.


pletch@cs.newpaltz.edu