Event Handling in JBuilder by Charlie Calvert

By: Charlie Calvert

Abstract: This is the first part of a three part article on Event Handling in JBuilder. The text is an excerpt from my book, Charlie Calvert's Learn JBuilder, from WordWare publishing.

Event Handling

Copyright © 2002 by Charlie Calvert

Go to Part I
Go to Part II
Go to Part III
Get the source for the whole book.

In this chapter and the next you are going to learn the basic facts about handling events. The text will cover four major topics, the first two in this chapter, the last two in the next:

  1. Basic Event Handling: Learn the mechanism that JBuilder employs to allow you to handle the most basic events generated by a particular class. For instance, you will learn how to respond to a click on a JButton. More importantly, you learn how the code that supports this kind of simple event handler is constructed, and the various options you will have when implementing it. In particular, you will have a chance to examine the syntax for both anonymous and standard adapter classes.

  2. Supporting Listener Interfaces: Learn to create a class that supports an interface such as MouseListener, KeyListener, or the JDK 1.4 class called MouseWheelListener. These interfaces can enable a class to respond to a wide range of events.

  3. Generating Events: Create a class that can not only receive events, but also fire events. This technology can be used as a form of loosely coupled communication between the classes in your program. Loose coupling of this kind is highly respected in the OOP programming world because it promotes reuse and helps develop robust architectures.

  4. Custom Events: Look at how to create custom events that are tailor-made to convey a particular type of information.

JBuilder provides easy to use mechanisms for handling most of these technologies. This chapter will explore these techniques, and give you some tips on how to expedite event-based development.

Java events are not always an easy subject, especially for people new to the Java language. The level of complexity involved is significant, but not mind-boggling. A modest level of effort in this area will yield big results in the long run. Furthermore, JBuilder makes this topic much simpler than it would be were you trying to write event-handling code by hand.

NOTE: Not all of the techniques shown in this chapter are supported in the Personal version of JBuilder. However, I will show all the code that the JBuilder wizards generate. If necessary, you can simply type the code in by hand. The MouseWheelListener class is found only in JDK 1.4.

Basic Event Handling

You already know the simplest way to handle events in JBuilder. However, we should go over it one more time just to be sure that we understand exactly how this code is structured.

NOTE: In this chapter you are going to learn how to make a JButton object respond to events. You will also learn about the ActionEvent object. I will not, however, include descriptions of how to make a JButton respond to key presses, or how to make it respond to a key combination, such as Alt-X. I will also not talk about changing the text of the button, using HTML with the button, or adding icons to the button. All of these latter subjects are discussed later in this section of the book. In particular, see Chapter 18, called "The Art of the Button."

Create a new project and place inside it a new application that includes a JMenuBar. The ability to create a menu bar as part of the application is built into the JBuilder Application wizard, which was described in the "Default Applications and Applets" chapter back in the Getting Started section of this book.

Make sure Frame1 has its layout set to the default BorderLayout. Drop a JButton down on Frame1. Make sure the constraints for the button are set to Center. At this stage, your application is all button. When you run it, it should look like the application shown in Figure 1.

Figure 1: A default JBuilder project with a button dropped down onto Frame1.

We now want to create a simple event that can be associated with this button. More specifically, when the user presses this button, we want our event to be called and some associated action to take place. For instance, we might pop up a dialog that will allow the user to choose a new application.

In JBuilder, there are two ways to associate an event with the button:

  1. Double click on the button. This will automatically take you to the source for your program and create a method handler stub for you.

  2. Turn to the Events page of the inspector and click on the actionPerformed property. A single click should give you an editable suggested name for your property. Use this single click technology to specify the name for the event handler you are about to create. You can go with the default if you want, but you can use this opportunity to give your event handler a more meaningful name. A double click or a press of the enter key will take you to source view and create the method stub for you.

Regardless of which course you take, the method stub that was created should look something like this, though the actual name of the method will vary:

void JButton1_actionPerformed(ActionEvent e)
{

 

}

Fill in the method with some simple code so that you can see it in action:

void JButton1_actionPerformed(ActionEvent e)

{
    JOptionPane.showMessageDialog(this, "Hate is not conquered by hate, hate is conquered by love!");

}

The JButton1_actionPerformed method will be called automatically when JButton1 is selected by the user.

NOTE: The JOptionPane class is designed to help you build pop up dialogs of various types. It has methods such as showMessageDialog, showInputDialog, showConfirmDialog and so on. This book is about JBuilder, and not about the JDK, so I will ask that you read the help or visit Sun's web site if you want to learn more about this class. There are many examples of using the class in the source code for this book, so you can grep through that code if you want to see the class in action.

NOTE: Before moving on to the next subject, I should perhaps mention that there are occasions when you will get error messages when trying to create an event handler. For instance, you might double click on JButton1 and receive a seemingly nonsensical error saying that a parenthesis was not found. If something like this happens to you, try closing the file you are working on, and then opening it again from the recently used files list. This usually fixes the problem for me.

How Events Get Called

The JButton1_actionPerformed method in the previous section is not called directly by the JDK. Instead, it is called by a class embedded inside of Frame1. It is now time to spend a moment thinking about that class.

The class that calls JButton1_actionPerformed can appear in one of two places in your source code. Which of the places it resides depends on the settings you selected in the Project Properties dialog reached by selecting Project | Project Properties | Code Style from the JBuilder menu. The dialog, which was mentioned in the "Product Overview" chapter, is shown in Figure 2.

Figure 2: Selecting the style for event-handling adapters in the Project Properties dialog.

To understand exactly what is happening now, you need to take a look at two copies of the complete source code for a JFrame with a button on it and one response method. The first version is found in Listing 1, the second in Listing 2.

Listing 1: The source for Frame1 as it appears when you use standard adapters.

package untitled32;
 
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
 
public class Frame2 extends JFrame
{
	JButton JButton1 = new JButton();
	 
	public Frame2()
	{
		try
		{
			jbInit();
		} catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	private void jbInit() throws Exception
	{
		JButton1.setText("JButton1");
		JButton1.addActionListener(new Frame2_JButton1_actionAdapter(this));
		this.getContentPane().add(JButton1, BorderLayout.CENTER);
	}
	 
	void JButton1_actionPerformed(ActionEvent e)
	{
		 
	}
}
 
class Frame2_JButton1_actionAdapter implements java.awt.event.ActionListener
{
	Frame2 adaptee;
	 
	Frame2_JButton1_actionAdapter(Frame2 adaptee)
	{
		this.adaptee = adaptee;
	}
	
	public void actionPerformed(ActionEvent e)
	{
		adaptee.JButton1_actionPerformed(e);
	}
}

Listing 2: The source for Frame1 as it appears when you use anonymous adapters.

package untitled32;
 
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
 
/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author unascribed
* @version 1.0
*/
 
public class Frame2 extends JFrame
{
	JButton JButton1 = new JButton();
	 
	public Frame2()
	{
		try
		{
			jbInit();
		} catch(Exception e)
		{
			e.printStackTrace();
		}
	}
    
	private void jbInit() throws Exception
	{
		JButton1.setText("JButton1");
		JButton1.addActionListener(new java.awt.event.ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				JButton1_actionPerformed(e);
			}
		});
		this.getContentPane().add(JButton1, BorderLayout.CENTER);
	}
	 
	void JButton1_actionPerformed(ActionEvent e)
	{
		 
	}
}

I created these two source files by first choosing File | New from the JBuilder menu. In the General page of the Object Browser, I then chose Frame, which created a new instance of the JFrame class. In Listing 1, the Code Style for Event Handling in the project was set to Standard Adapters, and in Listing 2, it was set to Anonymous Adapters. I then dropped a button on each frame and created a handler for it.

The reason I created new JFrame descendants rather than working with a Frame1 instance was so that these examples would not include irrelevant code such as the processWindowEvent method. This code is stripped down so that it shows little detail other than the code we want to focus on.

In Listing 1, notice that there is a class called Frame2_JButton1_actionAdapter:

class Frame2_JButton1_actionAdapter implements java.awt.event.ActionListener
{
	Frame2 adaptee;
	 
	Frame2_JButton1_actionAdapter(Frame2 adaptee)
	{
		this.adaptee = adaptee;
	}

	public void actionPerformed(ActionEvent e)
	{
		adaptee.JButton1_actionPerformed(e);
	}
}

Look further and you will see that this class calls the JButton1_actionPerformed method that we had created earlier:

adaptee.JButton1_actionPerformed(e);

What we have established so far is that there is a class called an adapter included in your source code that calls the button handler. The button handler is where you place the code you want to have called when the button is pressed.

So how does the JDK know to call your adapter when the button is pressed? The following code in jbInit supplies part of the answer to that question:

JButton1.addActionListener(new Frame2_JButton1_actionAdapter(this));

The addActionListener method exists deep inside the JDK. This method calls it, and assigns the Frame2_JButton1_actionAdapter class to this instance of the JButton class. The JButton1 class maintains a list of all the actionAdapters that have been registered with it. When the button itself is pressed, it looks at that list and calls the actionPerformed method of that class:

public void actionPerformed(ActionEvent e)
{
	adaptee.JButton1_actionPerformed(e);
}

So now the mystery has been resolved. We have established that the JDK knows to call the adapter class because you called addActionListener, and we have seen that the adapter class calls our JButton1_actionPerformed method. So far so good.

NOTE: You will, in fact, learn more about this process in the next chapter. There you will -- with the help of JBuilder -- create your own custom instances of the addActionListener method. But for now, you have enough information to see how the system works.

You might also be interested in viewing the first method of the adapter:

Frame2 adaptee;

Frame2_JButton1_actionAdapter(Frame2 adaptee)
{
	this.adaptee = adaptee;
}

This method is the constructor. It is passed an instance of the main frame for your application back in jbInit:

JButton1.addActionListener(new Frame2_JButton1_actionAdapter(this));

The word this is the reference to Frame2 that gets passed to the adapter's constructor.

The frame is then assigned to the variable adaptee. The adaptee variable is used in the actionPerformed method to call JButton1_actionPerformed.

NOTE: In case you are curious, I believe that adaptee is not an English word. It is, however, a proper French word. I'm sure that the multicultural and very sophisticated JBuilder developers were in fact thinking in French when they came up with this variable name!

At this stage, you now know the whole story of how events work when you use Standard Adapters in a JBuilder application. I believe that all of the code in this book, unless explicitly marked otherwise, uses standard adapters.

Anonymous Adapters

Anonymous Adapters, shown in Listing two, use an anonymous class instead of standard Java class. The anonymous class is created in jbInit:

JButton1.addActionListener
(
	new java.awt.event.ActionListener()
	{
		public void actionPerformed(ActionEvent e)
		{
			JButton1_actionPerformed(e);
		}

	}
);

Anonymous classes are created, declared and implemented inside the parameter of another method. The method in this case looks like this:

JButton1.addActionListener(-- class inserted here --)

These are standard parenthesis, as if you were simply passing a parameter to a method. The class that has been inserted into the parameter for this method looks like this:

new java.awt.event.ActionListener()
{
	public void actionPerformed(ActionEvent e)
	{
		JButton1_actionPerformed(e);
	}
}

Note the existence of the word new before the class declaration, and note that the code uses curly braces rather than parens, and note further that the ancestor class name is given, but the class itself has no name: it is anonymous. The class is derived from ActionListener, but the child class has no name.

As you can see, this class is very similar to the adapter class from Listing 1. It has no constructor and no Francophile adaptee variable. Nevertheless, its structure should be very familiar. The adaptee variable is not needed, since this class lives inside the scope of Frame2, and hence can call its methods directly. In fact, this class has an actionPerformed method that directly calls JButton1_actionPerformed:

public void actionPerformed(ActionEvent e)
{
	JButton1_actionPerformed(e);
}

Once again, the JButton1.addActionListener method inserts a handle to this anonymous class into a list maintained by JButton1. When JButton1 is pressed, all the classes in that list have their actionPerformed method called. What happens inside that method is up to us. In this case, we have that method call JButton1_actionPerformed. Or rather, JBuilder automatically generates code that will call JButton1_actionPerformed.

Anonymous Adapters vs Standard Adapters

When writing this book, and when developing the example programs, I had to decide whether I wanted to use anonymous adapters or standard adapters. As you already know, I chose to use standard adapters.

Why did I make that decision? Simply because to me Standard Adapters are easier to parse visually than anonymous adapters. Furthermore, the use of Standard Adapters helps to keep my jbInit methods free of clutter.

I make absolutely no claim that my choice here is better than its opposite. To my mind, this is simply a matter of taste, and my taste tends toward standard adapters. You should examine your own mind, and use the technique that you prefer. In the long run, it is all pretty much six of one, half dozen of the other. You should just pick your favorite technology and go along with it as happily as possible.

Go to Part I
Go to Part II
Go to Part III
Get the source for the whole book.


Server Response from: ETNASC02