Chapter 20. GUI Basics and Event Handling

  • All About Event Handling

  • Tips for Slimming Down Handler Code

  • Summary of Event Handling

  • Exercises

  • Some Light Relief—The Mouse That Roared

All GUI libraries have four basic areas of functionality:

  • Creation of user interface “controls” or components, such as scroll bars, buttons, and labels.

  • Support for giving behavior to the controls by connecting GUI events (for example, clicking a button) to code that you write.

  • Support for grouping and arranging the controls on the screen.

  • Support for accessing window manager facilities, such as specifying which window has the input focus, reading JPEG and other image files, and printing.

Related graphics libraries also provide support for graphics operations, such as drawing an arc, filling a polygon, and clipping a rectangle. There may even be a complete 2-D and 3-D drawing library, similar to the ones in Java.

For the first couple of major releases, Java supported GUI operations with a package called java.awt. The “AWT” stands for “Abstract Window Toolkit.” The AWT supported the portability goals of Java. The toolkit gave user programs a common binary window interface on systems with different native window systems. That's a very unusual feature, like having your favorite Macintosh program run on a Windows PC and still do GUI operations. You might wonder how it is done.

You can call methods in the AWT run-time to pop up a native menu, resize a window, get the location of a mouse click, and so on. Java then calls through to the native GUI library. Events coming back from the GUI go into the Java run-time, and are passed on to your code as appropriate. The Java bytecode is the same on each platform, but the native library behind the Java run-time library is specific to each platform. The AWT code uses the underlying native (or “peer”) window system to manipulate GUI objects.

The AWT is a series of abstract interfaces along with Java code to map them into the actual native code on each system. This is why it is an abstract window toolkit: it is not tailored for a specific computer, but it offers the same API on all. Too much different native behavior leaked through and, eventually IBM persuaded Sun to move to an all-Java GUI known as Swing. Swing is almost wholly implemented in Java. It uses native canvases on which to display widgets controlled and drawn by Java.

We mentioned the list of services that any GUI library must support. This chapter focuses on one of these services: an event-handling system that notices when the user adjusts one of the components and interrupts the program to tell it what happened.

You must know how to handle the events that controls can generate before you can make sense of the controls themselves. Event handling is a dull but necessary prerequisite, like eating your vegetables before you can have dessert. We will deal with controls in the next chapter. The basic idea with Java GUI programs is that you do the following actions:

  1. Declare controls. These controls include buttons, menus, and choices. You can extend these classes to add your own ideas/needs to the behavior, but this is often unnecessary.

  2. Implement event handlers to respond to clicking, menu selection, button presses, window re-sizing and other activity. Your implementing class will often be an anonymous class. Using an anonymous class reduces the amount of code and puts the class declaration close to where it is used.

  3. Add the controls to a container. You may choose to extend the container class, but this is frequently unnecessary. Containers display on the screen. Containers can hold several related controls that you want to appear next to each other onscreen. For example, a frame can hold two buttons and a scrolling panel.

It's a challenge to explain all this. The first three topics—declaring controls, handling their events, and putting them in containers—fit together so closely that you have to understand a bit of each before you can fully understand any of them. The topics are so big that each requires a whole chapter, and we will take them serially.

All About Event Handling

First, a few necessary words of explanation about the programming model for window systems. Unlike procedural programs in which things happen sequentially, windowing programs are asynchronous, because actions occur at unpredictable times. You never know which of the onscreen buttons, menus, frames, or other elements the user will touch next. Accordingly, a model known as event-driven programming is used.

In event-driven programming, the logic of your code is inverted. Instead of one flow of control from beginning to end, the run-time system sits in a “window main loop” simply waiting for user input. When the user clicks the mouse, the operating system passes it to the window manager which turns it into an event and passes it on to a handler you supplied earlier. This is known as a callback. Your handler is a callback routine, because the window system calls back to it when the event happens. Your event handler deals with the graphics event and any work associated with it.

If a button says “press here to read the file,” your code must arrange for the file to be read when called. Handling a button event means noticing that it occurred and doing the associated action, but other events may involve some drawing on the screen. For example, dragging something with the mouse is just repeatedly drawing it under the mouse coordinates as it moves.

Java event model

The event model is the name for the framework that turns a GUI interaction (mouse click, menu selection, button press, etc.) into a call for your code to process it. The event model can also be used for something unrelated to the GUI, like a timer going off. In other words, the event model is the design for connecting your code to any kind of asynchronous actions, called events, for handling.

The window manager can't directly call your event-handling routines because the run-time library doesn't even see your code until it is asked to run it. Therefore, at run-time the event model has to be told which of your routines handle events.

Java originally used inheritance to tie together your code and the event model. JDK 1.1 introduced a better approach called the delegation-based model. Some of the Java documentation still refers to “1.1-style events,” which is the current model. To get any events, your code has to begin by telling the window system, “send those events of yours to these methods of mine”. You connect the controls that generate events by registering a callback with your event-handling classes, as Figure 20-1 shows.

Figure 20-1. How events are passed in JDK 1.1

image

image

You register your handler code once at the beginning with the GUI object that will be generating events. After that, those events are fired (or sent) to you when they occur. In particular, your handler code is called and passed the event object as an argument.

If you do not completely understand callbacks, go back and read that section in Chapter 11. It is essential that you understand callbacks because they form the basis of the new event-handling model.

The events that are fired from the source to the listener are simply objects passed as arguments to a method call. An event object has several data fields holding information, such as where the event took place on the screen, a description of the event, the number of mouse clicks, the state of a checkbox, and so on. There is a general java.util.EventObject type and all AWT events are children of that, as Figure 20-2 shows.

Figure 20-2. The hierarchy of event objects in JDK 1.1

image

The JFrame example

A JFrame, a subclass of the AWT class Frame, has a title string and a menu bar on which you can add several menus. Following is the code to put a JFrame on the screen from an application:

import javax.swing.*;
public class FrameDemo {
     public static void main(String[] args) {
          JFrame jframe = new JFrame("Example");
          jframe.setSize(400,100);
          jframe.setVisible(true);
     }
}

Compiling and executing this program results in a frame similar to Figure 20-3 to appear on the screen. Here, we just gave it the name on the title bar of “Example.”

Figure 20-3. The JFrame is a subclass of Frame, which is a subclass of Window

image

Since it is a Window, JFrame is capable of receiving events generated by Windows. Clicking a Window to close it generates an event. If you try this with the program we described, you'll notice that the JFrame goes away but your program stays active. You must press Control-C to exit the program because you need to write code to handle the “window closing” event. The code should quit the program when it is invoked.

Let's add an event handler to our JFrame that notices when the WindowClosing event takes place. That event will be delivered when you click to close a Window.

First, tell our JFrame that Window events are handled by an object that I call “mwh” here—a nice short name which is an abbreviation of “my window handler.” The code appears exactly as before, and we add the following line to the main() routine:

jframe.addWindowListener(mwh);

The callback for a Window event is registered by calling the AWT method addWindowListener. This is a method in the Window superclass of JFrame that takes as a parameter an interface called WindowListener. Your applet must declare an instance of a class that implements the WindowListener interface and that will be the event handler for this button. You still have to write that class, but assuming it has the name shown below, you can instantiate an object according to the following example:

myCodeToHandleWinClose mwh = new myCodeToHandleWinClose();

The last step is to write the myCodeToHandleWinClose class that implements the java.awt.event.WindowListener interface and provides the promised methods. The class can be in a separate file or contained in the same one.

class myCodeToHandleWinClose implements WindowListener {
     public void windowClosing(WindowEvent e) {System.exit(0);}
     public void windowClosed(WindowEvent e) { }
     public void windowOpened(WindowEvent e) { }
     public void windowIconified(WindowEvent e) { }
     public void windowDeiconified(WindowEvent e) { }
     public void windowActivated(WindowEvent e) { }
     public void windowDeactivated(WindowEvent e) { }
}

The windowClosing() method of the myCodeToHandleWinClose class is called whenever the Window “close” choice is made. Notice that it was necessary to provide declarations for all the routines in the interface. Since we are interested only in WindowClosing, there are empty bodies for the other methods. We'll show a way of simplifying this later in the chapter.

The WindowListener interface (like many of the SomethingListener interfaces) is declared in the java.awt.event package. There are other event and Listeners declared in the javax.swing.event package. The WindowListener interface promises that there will be half a dozen methods with specific names. The methods will be called for different Window events now that we have delegated the task to the object “mwh” that contains the methods.

One advantage of the Java event framework is that the GUI-related code is easily separated from the application logic code. This flows from good use of OOP: put everything in a class of its own and declare instances of the class as needed. In this way, encapsulation works for you. If you want to change what happens on window close, you just update the class that deals specifically with it. In a non-OOP implementation, it's too easy to mix everything together, making program maintenance and testing ten times harder than it should be. The same event framework is used for JavaBeans (component software), as well as the AWT.

Putting the whole thing together and into our JFrame example, the following code handles the window closing event:

import javax.swing.*;
import java.awt.event.*;
public class CloseDemo {
     public static void main(String[] args) {
          JFrame jframe = new JFrame("Example");
          jframe.setSize(400,100);
          jframe.setVisible(true);
          myCodeToHandleWinClose m = new myCodeToHandleWinClose();
          jframe.addWindowListener(m);
    }
}

class myCodeToHandleWinClose implements WindowListener {

     public void windowClosing(WindowEvent e) {System.exit(0);}
     public void windowClosed(WindowEvent e) { }
     public void windowOpened(WindowEvent e) { }
     public void windowIconified(WindowEvent e) { }
     public void windowDeiconified(WindowEvent e) { }
     public void windowActivated(WindowEvent e) { }
     public void windowDeactivated(WindowEvent e) { }
}

Notice that the code inside the WindowClosing method calls System.exit() to quit the program. When you run this, you'll see the same frame as before, but when you click on the window to close it, the program will now exit gracefully. Well done! You have finished your first example of an event handler.

You may wonder what events exist, what interfaces deal with them, and what methods the Listener interfaces use. Table 20-1 summarizes the answers to all three questions. You should also review the javadoc pages for classes and interfaces in package java.awt.event. The source code for the interfaces and their methods can also be reviewed in the directory $JAVAHOME/src/java/awt/event.

Table 20-1. Categories, events, and interfaces

General category

Events that it generates

Interface that the event-handle implements

Mouse

Dragging or moving mouse causes a MouseEvent.

MouseMotionListener

Clicking, selecting, releasing causes a MouseEvent.

MouseListener

Mouse wheel

Mouse wheel events (new in 1.4).

MouseWheelListener

Keyboard

Key press or key release causes a KeyEvent.

KeyListener

Selecting (an item from a list, checkbox, etc.)

When item is selected causes an ItemEvent.

ItemListener

Text input controls

When newline is entered causes a TextEvent.

TextListener

Scrolling controls

When a scroll bar slider is moved causes an AdjustmentEvent.

AdjustmentListener

Other controls (button, menu, etc.)

When pressed causes an ActionEvent.

ActionListener

Window changes

Open, close, iconify, etc., causes a WindowEvent.

WindowListener

Keyboard focus changes

Tabbing to next field or requesting focus causes a FocusEvent. A component must have the focus to generate key events.

FocusListener

Component change

Resizing, hiding, revealing, or moving a component causes a ComponentEvent.

ComponentListener

Container change

Adding or removing a component to a container causes a ContainerEvent.

ContainerListener

The same framework is used by all the event handlers:

  1. Write a class that implements a SomethingListener interface.

  2. Declare an object (for example, myHandler) of your class.

  3. On your component, call the addSomethingListener(myHandler) method.

You can reduce the amount of code with inner classes. See ips for Slimming Down Handler Code on page 508.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.54.245