Event Handling

The problem: connecting user events to code execution.

User events: mouse click, mouse down, mouse up, mouse drag, mouse within a component (focus), key pressed, particular key pressed, etc.

Solution 1: have controls listen to all events, check whether they should respond, and then respond. Roughly this was the JDK 1.0 model.

Solution 2: Tell controls what events they can respond to and have them direct messages to appropriate actions (methods within object) . This is the "delegation" model of JDK 1.1 and is more efficient.

Model:
user action --> source object (control) ----> target object (listener)

What we need to do is:

  1. identify (turn on) controls that will respond to events
  2. For these controls, connect them to appropriate listeners. This is called registration. Note multiple actions can take place for each event, and these are not synchronized. Also any object can be a listener if it implements the appropriate interface.

So what do we need to do?

  1. To the control c, execute c.addSomeListener(some obj).

    This makes c respond to the event by sending a message to the object.

  2. Make obj an event listener, i.e. by
    1. declaring that it implement the appropriate Listener Interface
    2. defining the methods of the Interface.

Abstract Example: Making object c of Foo respond to Button b;

      Foo c = new Foo();
      Button b = new Button("clear");
      b.addActionListener(c);       //.. buttons generate action events
     ...
      // ActionListener has only 1 method, actionPerformed
     ..
      class Foo implements ActionListener

   {
    ...
        public void  actionPerformed()
     {
        whatever foo should do when button is pressed
     }
    ...
   }

Four Designs


This program allows the user to draw lines, clear the screen, and change colors.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

// Design 1:  Everyone talks to applet and gets messages from applet.
//   Applet acts as god class.  This is usually the simpliest design.


class MyCanvas extends Canvas
{
  Color linecolor= Color.black;  

  public void paint(Graphics g)  
  {
      Dimension d = getSize();
      g.drawRect(2,2,d.width-4,d.height-4);
  }

  public void clear()
 {
   Graphics g = getGraphics();
   Dimension d = getSize();
   Color c = getBackground();
   g.setColor(c);
   g.fillRect(0,0,d.width,d.height);
   repaint();
   //.. repaint() schedules update(). update() (typically) calls paint().
 }

  public void changeColor()
 {
   Color[] colors ={Color.red,Color.black,Color.blue,Color.magenta};
   linecolor = colors[random(colors.length)]; 
 }
 
 public int random(int i)
  {
     return (int) Math.floor(Math.random()*i);
  }

  public void drawLine(Point p, Point q)
  {
   Graphics g = getGraphics();
   g.setColor(linecolor);
   g.drawLine(p.x,p.y,q.x,q.y);
   g.drawOval(q.x,q.y,2,2);
  }
 public void drawOval(Point p)
  {
    Graphics g = getGraphics();
    g.drawOval(p.x,p.y,2,2);
  }

}

public class MouseTest extends Applet
  implements ActionListener, MouseListener
{
  Panel top;
  Button clear;
  Button change;
  MyCanvas mycanvas;
  Point begin,end;

  public void init()
 {
   top = new Panel();
   clear = new Button("Clear");
   clear.addActionListener(this);
   change = new Button("Change Color");
   change.addActionListener(this);
   top.add(new Label("Mouse click to draw lines"));
   top.add(clear);
   top.add(change);
   setLayout(new BorderLayout());
   add(top,"North");
   mycanvas = new MyCanvas();
   mycanvas.addMouseListener(this);
   add(mycanvas,"Center");
 }

  public void actionPerformed(ActionEvent actevt)
 {
   Object arg = actevt.getSource();
   if (arg == clear ) mycanvas.clear();
    else if (arg == change) mycanvas.changeColor();
 }

   public void mousePressed(MouseEvent me)
 {
    begin = me.getPoint();
    mycanvas.drawOval(begin);
 }
    public void mouseReleased(MouseEvent me )
  {
     end = me.getPoint();
     mycanvas.drawLine(begin,end);
   }

   public void mouseClicked(MouseEvent me)
  {}

   public void mouseEntered(MouseEvent me)
  {}

  public void mouseExited(MouseEvent me)
 {}
}
In the next design we group related activities more finely. The code is longer, but may allow easier changes. Code is simpliest when its structure reflects the organization of the computation.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;


class MyCanvas extends Canvas
{
  Color linecolor= Color.black;  


  public void paint(Graphics g)  // why doesn't this work?
  {
      Dimension d = getSize();
      g.drawRect(2,2,d.width-4,d.height-4);
  }


  public void clear()
 {
   Graphics g = getGraphics();
   Dimension d = getSize();
   Color c = getBackground();
   g.setColor(c);
   g.fillRect(0,0,d.width,d.height);
   repaint();
 }

  public void changeColor()
 {
   Color[] colors ={Color.red,Color.black,Color.blue,Color.magenta};
   linecolor = colors[random(colors.length)]; 
 }
 
 public int random(int i)
  {
     return (int) Math.floor(Math.random()*i);
  }

  public void drawLine(Point p, Point q)
  {
   Graphics g = getGraphics();
   g.setColor(linecolor);
   g.drawLine(p.x,p.y,q.x,q.y);
   g.drawOval(q.x,q.y,2,2);
  }
 public void drawOval(Point p)
  {
    Graphics g = getGraphics();
    g.drawOval(p.x,p.y,2,2);
  }

}

class ClearButton extends Button
{
   ClearButton(MouseTest mt)
  {
    super("Clear");
    addActionListener(mt);
  }

}
class ChangeButton extends Button
{
   ChangeButton(MouseTest mt)
  {
   super("Change Color");
   addActionListener(mt);
  }
}



public class MouseTest extends Applet
  implements ActionListener, MouseListener
{
  Panel top;
  ClearButton clear;
  ChangeButton change;
  MyCanvas mycanvas;
  Point begin,end;

  public void init()
 {
   top = new Panel();
   clear = new ClearButton(this);
   change = new ChangeButton(this);
   top.add(new Label("Mouse click to draw lines"));
   top.add(clear);
   top.add(change);
   setLayout(new BorderLayout());
   add(top,"North");
   mycanvas = new MyCanvas();
   mycanvas.addMouseListener(this);
   add(mycanvas,"Center");
}

  public void actionPerformed(ActionEvent actevt)
 {
   Object arg = actevt.getSource();
   if (arg == clear ) mycanvas.clear();
    else if (arg == change) mycanvas.changeColor();
 }

   public void mousePressed(MouseEvent me)
 {
    begin = me.getPoint();
    mycanvas.drawOval(begin);
 }
    public void mouseReleased(MouseEvent me )
  {
     end = me.getPoint();
     mycanvas.drawLine(begin,end);
   }

   public void mouseClicked(MouseEvent me)
  {}

   public void mouseEntered(MouseEvent me)
  {}

  public void mouseExited(MouseEvent me)
 {}
}

In this design we examine the interactions between the events.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

/* Design 2: here we analyze the objects and see where message are going
 The  Listener for the message must be able to see the object generating
 the message. 
Message Diagram:
                 clearbutton --> canvas  
                  changebutton --> canvas
                  canvas -->(mouse clicks) -->canvas
So the buttons will still need to be mediated by the applet, while
the canvas can talk to itself.
*/		  


class MyCanvas extends Canvas
     implements  MouseListener
{
  Color linecolor= Color.black;  //ugh
  Point begin,end;



  public void paint(Graphics g)  
  {
      Dimension d = getSize();
      g.drawRect(2,2,d.width-4,d.height-4);
  }


  public void clear()
 {
   Graphics g = getGraphics();
   Dimension d = getSize();
   Color c = getBackground();
   g.setColor(c);
   g.fillRect(0,0,d.width,d.height);
   repaint();
 }

  public void changeColor()
 {
   Color[] colors =
            {Color.red,Color.black,Color.blue,Color.magenta,Color.green};
   linecolor = colors[random(colors.length)]; 
 }
 
 public int random(int i)
  {
     return (int) Math.floor(Math.random()*i);
  }

  public void drawLine(Point p, Point q)
  {
   Graphics g = getGraphics();
   g.setColor(linecolor);
   g.drawLine(p.x,p.y,q.x,q.y);
   g.drawOval(q.x,q.y,2,2);
  }

 public void drawOval(Point p)
  {
    Graphics g = getGraphics();
    g.drawOval(p.x,p.y,2,2);
  }
   public void mousePressed(MouseEvent me)
 {
    begin = me.getPoint();
    drawOval(begin);
 }
    public void mouseReleased(MouseEvent me )
  {
     end = me.getPoint();
    drawLine(begin,end);
   }

   public void mouseClicked(MouseEvent me)
  {}

   public void mouseEntered(MouseEvent me)
  {}

  public void mouseExited(MouseEvent me)
 {}

}


public class MouseTest extends Applet
  implements ActionListener
{
  Panel top;
  Button clear;
  Button change;
  MyCanvas mycanvas;


  public void init()
 {
   mycanvas = new MyCanvas();  
   top = new Panel();
   clear = new Button("Clear");
   clear.addActionListener(this);
   change = new Button("Change Color");
   change.addActionListener(this);
   top.add(new Label("Mouse click to draw lines"));
   top.add(clear);
   top.add(change);
   setLayout(new BorderLayout());
   add(top,"North");
   mycanvas.addMouseListener(mycanvas);
   add(mycanvas,"Center");
 }

  public void actionPerformed(ActionEvent actevt)
 {
   Object arg = actevt.getSource();
   if (arg == clear) mycanvas.clear();
    else if (arg == change) mycanvas.changeColor();
 }
}



Example redone with Inner Classes

Inner classes are a recent addition to Java.
Each inner class is local to the class but has access to all the members of the class.
Inner classes seem to violate the principle of information hiding.
Inner classes are not the same as nested classes in C++.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

// Version using inner classes. Arguable bad style.

public class MouseTest extends Applet
 
{
  Panel top;
  Button clear;
  Button change;
  MyCanvas mycanvas;

  public void init()
 {
   mycanvas = new MyCanvas();  
   top = new Panel();
   clear = new Button("clear");
   clear.addActionListener(mycanvas);
   change = new Button("Change Color");
   change.addActionListener(mycanvas);
   top.add(new Label("Mouse click to draw lines"));
   top.add(clear);
   top.add(change);
   setLayout(new BorderLayout());
   add(top,"North");
   mycanvas.addMouseListener(mycanvas);
   add(mycanvas,"Center");
 }


class MyCanvas extends Canvas
     implements  MouseListener, ActionListener //.. note: inner class
{
  Color linecolor= Color.black; 
  Point begin,end;


  public void paint(Graphics g)
  {
      Dimension d = getSize();
      g.drawRect(2,2,d.width-4,d.height-4);
  }


  public void clear()
 {
   Graphics g = getGraphics();
   Dimension d = getSize();
   Color c = getBackground();
   g.setColor(c);
   g.fillRect(0,0,d.width,d.height);
   repaint();
 }

  public void changeColor()
 {
   Color[] colors =
            {Color.red,Color.black,Color.blue,Color.magenta,Color.green};
   linecolor = colors[random(colors.length)]; 
 }
 
 public int random(int i)
  {
     return (int) Math.floor(Math.random()*i);
  }

  public void drawLine(Point p, Point q)
  {
   Graphics g = getGraphics();
   g.setColor(linecolor);
   g.drawLine(p.x,p.y,q.x,q.y);
   g.drawOval(q.x,q.y,2,2);
  }

 public void drawOval(Point p)
  {
    Graphics g = getGraphics();
    g.drawOval(p.x,p.y,2,2);
  }
   public void mousePressed(MouseEvent me)
 {
    begin = me.getPoint();
    drawOval(begin);
 }
    public void mouseReleased(MouseEvent me )
  {
     end = me.getPoint();
    drawLine(begin,end);
   }

   public void mouseClicked(MouseEvent me)
  {}

   public void mouseEntered(MouseEvent me)
  {}

  public void mouseExited(MouseEvent me)
 {}

  public void actionPerformed(ActionEvent actevt)
 {
   Object arg = actevt.getSource();
   if (arg == clear) clear();
    else if (arg == change) changeColor();
 }
}
}

Event processing, in review

Recipe for making object2 responds to events in object1

Controls, Listeners, and interface method


Control Registers Interest Receives Event
Button addActionListener(..) actionPerformed
List
MenuItem
TextField
Checkbox addItemListener itemStateChanged
Choice
List
CheckBoxMenuItem
Dialog Frame addWindowListener windowClosing
windowOpening
windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactivated
Dialog Frame addComponentListener componentMoved
componentHidden
componentResized
componentShown
ScrollBar addAdjustmentListener adjustmentValueChanged
Canvas addMouseListener mousePressed
Dialog mouseReleased
Frame mouseEntered
Panel mouseExited
Window mouseClicked
Canvas addMouseMotionListener mouseDragged
Dialog mouseMoved
Frame
Panel
Window
Component addKeyListener keyPressed
keyReleased
keyTyped
Component addFocusListener focusGained
focusLost
TextComponent addTextListener textValueChanged

Table Use

The first column names the control.
The second column tells how to awaken control on a particular event and who to send the message to when the event takes place.
The third column tells what method(s) needs to implemented by the object which is responded to the message.
Focus is the component that can accept user input.