Table of Contents

Event Driven Programming

Writing GUI applications requires that program control be driven by the user's interaction with the GUI. When the user moves the mouse, clicks on a button, or selects an item from a menu an “event” occurs.

Event driven programs contain two specific types of objects:

The operating system or windowing system captures generated events and notifies the listener objects when a type of event they are listening has occurred. The operating system notifies the event listener object by sending a message to (calling) the event handling method. If no event listener object is subscribed to listen for the event that occurred, the event is ignored.

Types of Events

There are many different kinds of events

Event Trigger Event Listener Type
Click a button ActionListener
Press Enter while typing in a text field ActionListener
Choose a menu item ActionListener
Close a frame (main window) WindowListener
Press mouse button while hovering over a component MouseListener
Move mouse over a component MouseMotionListener
Component becomes visible ComponentListener
Component gets keyboard focus FocusListener
Table/List selection changes ListSelectionListener
Any property in a component changes* PropertyChangeListener

*For example, the text associated with a label in a component changes.

Action Events I

One of the most common type of events is an action event.

Sample Code

Here is an example of a GUI class that is an Action Listener:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
public class GUI implements ActionListener {
 
  private JButton btnQuit;
  private JButton btnHelp;
 
  public static void main(String[] args) {
    GUI myGUI = new GUI();
  }
 
  public GUI() {
 
    JFrame frame = new JFrame("Event handing sample");
    frame.setSize(200, 100);
    frame.setLocation(10, 10);
    frame.setResizable(false);
    frame.setLayout(null);
 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
    frame.setVisible(true);
 
    btnQuit = new JButton("Quit");
    btnQuit.addActionListener(this);
    btnQuit.setBounds(10, 25, 80, 30);
 
    frame.add(btnQuit);
 
    btnHelp = new JButton("Help");
    btnHelp.addActionListener(this);
    btnHelp.setBounds(100, 25, 80, 30);
 
    frame.add(btnHelp);
 
  }
 
  public void actionPerformed(ActionEvent event)
  {
    System.out.println("You pushed my button");
  }
}

The class contains two Action Sources (btnQuit and btnHelp). In order for the GUI class to be an action listener, it must implement the ActionListener interface. This is accomplished by:

This is not a good way to design your code because the GUI class now serves two purposes: 1) describe the layout of the window and 2) describe the functionality of the components of the window. Next we will attempt to separate the Action Listener from the GUI class.

Identifying the Event Source

The sample above displays “You pushed my button” whenever either button is pressed. In fact, the same message would be displayed even if the something other than an Action Event occurred. We can ensure that the event source was a JButton with the following modification:

public void actionPerformed(ActionEvent event)
{
  if(event.getSource() instanceof JButton) {
    System.out.println("You pushed my button");
  }
}

The getSource() method returns a reference to the source of the event that triggered the call to actionPerformed. The conditional will be true if the source object is an instance of JButton, i.e., if the source object is an object from the JButton class. We can do even better than this though. The following modification allows us to determine exactly which button was the source of the event.

public void actionPerformed(ActionEvent event)
{
  if(event.getSource() instanceof JButton) {
    JButton eventSrc = (JButton)event.getSource();
    if(eventSrc == btnQuit)
    {
      System.out.println("You pushed my QUIT button");
    } else if(eventSrc == btnHelp) {
      System.out.println("You pushed my HELP button");
    }
  }
}

Alternatively, we could use getActionCommand() to get the text of the source object instead of getting a reference to the source object. This would look something like this:

public void actionPerformed(ActionEvent event)
{
  if(event.getSource() instanceof JButton) {
    String text = event.getActionCommand();
    if(text.equals("Quit")
    {
      System.out.println("You pushed my QUIT button");
    } else if(text.equals("Help") {
      System.out.println("You pushed my HELP button");
    }
  }
}

We now know how to handle events in our code; however, our GUI class handles two distinct tasks:

This is not a good software design. Separating these tasks into to distinct classes is a better design decision.

Also, for GUI classes with a lot of components, the logic associated with the actionPerformed() method could get pretty complicated with lots of if statements required to identify the component that caused the event.

Action Events II

Given the problems identified with implementing the ActionListener in the GUI class, we will explore making separate classes to handle action events. We can replicate the functionality of the example in the previous section by creating an separate action listener class:

import java.awt.event.*;
import javax.swing.JButton;
 
public class ButtonEventHandler implements ActionListener {
 
  private GUI refToGUI;
 
  public ButtonEventHandler(GUI guiRef)
  {
    refToGUI = guiRef;
  }
 
  public void actionPerformed(ActionEvent event)
  {
    if(event.getSource() instanceof JButton) {
      JButton eventSrc = (JButton)event.getSource();
      if(eventSrc == refToGUI.btnQuit)
      {
        System.out.println("You pushed my QUIT button");
      } else if(eventSrc == refToGUI.btnHelp) {
        System.out.println("You pushed my HELP button");
      }
    }
  }
}

Unfortunately, in order for us to make this code work, ButtonEventHandler must be able to access the JButton attributes of the GUI class. We'll need to modify our GUI class as follows:

import javax.swing.*;
 
public class GUI {
 
  public JButton btnQuit;  // ALERT: Bonehead move
  public JButton btnHelp;  // ALERT: Bonehead move
 
  private ButtonEventHandler btnEventHandler;
 
  public static void main(String[] args) {
    GUI myGUI = new GUI();
  }
 
  public GUI() {
    btnEventHandler = new ButtonEventHandler(this);
 
    JFrame frame = new JFrame("Event handing sample");
    frame.setSize(200, 200);
    frame.setLocation(10, 10);
    frame.setResizable(false);
 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
    frame.setVisible(true);
 
    btnQuit = new JButton("Quit");
    btnQuit.addActionListener(btnEventHandler);
    btnQuit.setBounds(10, 125, 80, 30);
 
    frame.add(btnQuit);
 
    btnHelp = new JButton("Help");
    btnHelp.addActionListener(btnEventHandler);
    btnHelp.setBounds(100, 125, 80, 30);
 
    frame.add(btnHelp);
 
  }
}

The modifications are:

Notice:

Action Events III

Action Events II is a step in the right direction, but it violates one of the tenants of Object Oriented programming: don't let others see or mess with your private attributes. We can overcome this weakness by making use of an inner class.

Inner Classes

For example:

public class Outer {
 
  private int outerAttr = 0;
  private Inner in;
 
  private class Inner {
    private int innerAttr = 0;
 
    private void doStuff()
    {
      ++innerAttr;
      --outerAttr;
    }
  }
 
  public static void main(String[] args)
  {
    Outer out = new Outer();
    out.displayStuff();
  }
 
  public Outer()
  {
    in = new Inner();
  }
 
  public void displayStuff()
  {
    System.out.println("Inner: " + in.innerAttr + " Outer: " + outerAttr);
    in.doStuff();
    System.out.println("Inner: " + in.innerAttr + " Outer: " + outerAttr);
  }
}

Will produce:

Inner: 0 Outer: 0
Inner: 1 Outer: -1

Sample Code

Making use of the getActionCommand() method of the ActionEvent object passed to actionPerformed(), we no longer need the references to the buttons to be attributes of the class. We can declare them locally in the GUI class's constructor.

To make things slightly more interesting, we'll add a JLabel to the GUI that displays a count of the number of times the Help button has been clicked. Each time the Help button is clicked, the count is incremented and the JLabel is updated. Here is the grand finale:

public class GUI {
 
  private JLabel helpCountLbl;
 
  private class ButtonEventHandler implements ActionListener {
 
    private int helpCount = 0;
 
    @Override
    public void actionPerformed(ActionEvent event) {
      if(event.getSource() instanceof JButton) {
        String actionCommand = event.getActionCommand();
        System.out.println(actionCommand);
        if(actionCommand.equals("Quitter")) {
          System.out.println("You pushed the Quit button");
        } else if(actionCommand.equals("Help")) {
          System.out.println("You pushed my real help button");
          ++helpCount;
          String helpLbl = helpCount == 1 ? " cry " : " cries ";
          helpCountLbl.setText(helpCount + 
              helpLbl + "for help");
        }
      }
    }
  }
 
  public static void main(String[] args) {
    GUI myGUI = new GUI();
  }
 
  public GUI() {
    JButton btnQuit;
    JButton btnHelp;
 
    ButtonEventHandler btnHandler = new ButtonEventHandler();
    JFrame frame = new JFrame("Event handing sample");
    frame.setSize(200, 100);
    frame.setLocation(10, 10);
    frame.setResizable(false);
    frame.setLayout(null);
 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
    frame.setVisible(true);
 
    btnQuit = new JButton("Quit");
    btnQuit.setActionCommand("Quitter");
    btnQuit.addActionListener(btnHandler);
    btnQuit.setBounds(10, 25, 80, 30);
 
    frame.add(btnQuit);
 
    btnHelp = new JButton("Help");
    btnHelp.addActionListener(btnHandler);
    btnHelp.setBounds(100, 25, 80, 30);
 
    frame.add(btnHelp);
 
    helpCountLbl = new JLabel("No cries for help");
    helpCountLbl.setBounds(10, 50, 180, 30);
 
    frame.add(helpCountLbl);
  }
 
}

Note: