Swing Events

In the Java Swing GUI, interaction with the user is communicated through Events and Listeners.

Events can be low-level, like a mouse movement or click, or a keyboard type; or they can be high-level, like a button click or a menu selection. In both cases the mechanism is the same.

An event is produced by an event generator (typically a GUI component), and sent by the generator to every event listener that has registered on the component to receive that kind of event.

  1. To handle events of type FooEvent, some object (the listener) must implement FooListener. This might involve writing a method called public void fooChanged(FooEvent evt).
  2. The event producer's addFooListener(FooListener listener) method is called to add the FooListener to the producer's listener list.
  3. When a FooEvent occurs, the producer calls the fooChanged method of each of the FooListeners it knows about, one after the other.

That's pretty much it. You create a listener of an appropriate type, tell an event generater about it, and the event generator will call your listener when an event occurs.

Five ways to implement a listener

Let's get concrete about these things. A JButton is an ActionEvent producer. You can tell this because JButton has a method called addActionListener that takes an ActionListener as an argument. So, if you had an ActionListener, you could get the JButton to call its actionPerformed method.

So how do you get an ActionListener? Well, there are a whole mess of different ways, and each illustrates something interesting about interfaces or inner classes.

Below is some basic code for us to get started with. Scroll down so that you can read the entire block of code, then click the numbered tabs to see different ways of solving the problem.

public class ListenerDemo {                               

    public ListenerDemo() {
        JButton jb = new JButton("Hi");
        // <-- need to assign a listener here.
    }








    
    // we want this code to get called
    // when the button is pushed.
    private void reply() {
        System.out.println("Why, hello!");
    }
}

[Note that there is code missing that actually adds the JButton to some frame to display it.]

This code won't do anything when you push the button, because the button was never given a listener. The button will look like it's pushing just fine, but when you release, the button won't know who to notify about the event. In order to fix this, we need to add an ActionListener to the button.

public class ListenerDemo implements ActionListener {     

    public ListenerDemo() {
        JButton jb = new JButton("Hi");
        jb.addActionListener(this);
    }


    public void actionPerformed(ActionEvent evt) {
        reply();
    }    
    
    
    
    
    
    // we want this code to get called
    // when the button is pushed.
    private void reply() {
        System.out.println("Why, hello!");
    }
}

One simple way to get an action listener is to implement it yourself in the class. Here, the ListenerDemo itself implements ActionListener, and has the new actionPerformed method to prove it. Since this ListenerDemo class is now an ActionListener, we can just call jb.addActionListener(this), and the button will call our own actionPerformed method when the button is pressed.

public class ListenerDemo {                          

    public ListenerDemo() {
        JButton jb = new JButton("Hi");
        jb.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                reply();
            }
        });
    }
    
    
    
    
    
    // we want this code to get called
    // when the button is pushed.
    private void reply() {
        System.out.println("Why, hello!");
    }
}

And now we've compressed #3 even further. Instead of a separate inner class definition, we now define the class on the fly at the moment we create a new instance of it. This is called an anonymous inner class. You can read it as a class that implements or extends the class specified after new (ActionListener in this case). Here, we're implementing ActionListener's one method.

public class ListenerDemo {                               

    public ListenerDemo() {
        JButton jb = new JButton("Hi");
        jb.addActionListener(myListener);
    }

    private ActionListener myListener =
        new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                reply();
            }
        }
    };
    
    // we want this code to get called
    // when the button is pushed.
    private void reply() {
        System.out.println("Why, hello!");
    }
}

This one is an alternative to #2. Like #2, we still have a field that holds the listener, but this time we define the listener class anonymously instead of explicitly.

public class ListenerDemo {                               

    public ListenerDemo() {
        JButton jb = new JButton("Hi");
        jb.addActionListener(new MyActionListener());
    }

    class MyActionListener implements ActionListener {
        public void actionPerformed(ActionEvent evt) {
            reply();
        }
    }
    
    
    
    // we want this code to get called
    // when the button is pushed.
    private void reply() {
        System.out.println("Why, hello!");
    }
}

Here, we've taken the same approach as #2, but instead of defining a separate field to hold the class, we just create a new instance of the class in the addActionListener method, where we need it.

public class ListenerDemo {                               

    public ListenerDemo() {
        JButton jb = new JButton("Hi");
        jb.addActionListener(myListener);
    }

    class MyActionListener implements ActionListener {
        public void actionPerformed(ActionEvent evt) {
            reply();
        }
    }
    
    private ActionListener myListener = new MyActionListener();
    
    // we want this code to get called
    // when the button is pushed.
    private void reply() {
        System.out.println("Why, hello!");
    }
}

Sometimes you don't want to expose a listener method as part of your class. In that case, you can use an inner class instead. An inner class looks just like a normal class, except that it typicaly isn't public, and it's completely contained within its parent class. Here, we create a very simple class called MyButtonListener that just implements the single method of ActionListener. Once we've defined the class, we can create a new private field (myListener) holding the listener and pass it to the button. Note that the inner class can call methods of the outer class.