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.
|
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.
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.