programming.java--Lab 6 Chapter 6 Events and Actions Most of our lablets--indeed, most Java programs--take advantage of Java's facilities for producing graphical, user-friendly interfaces based on widgets and containers. Java's support for GUI building blocks doesn't stop there, though. Built into every applet are the methods needed to make our widgets respond to a full range of interface events. In this chapter, we use two lablets to illustrate how one writes programs that not only display the interface elements that we want on the screen, but then actually respond to our button clicks, key presses, and mouse movements. The first lablet, GalaEvents, is an extension of our lablet from Chapter 3, Gigobite. It demonstrates how to add event handlers to an existing applet so that the applet recognizes and responds to interface events. GalaEvents doesn't do much in response to these events, but at least it recognizes them. The second lablet, SketchPad, is a more complete example that not only recognizes interface events, but responds to them in interesting and useful ways. Lab Objectives In this lab, you will: Run and experiment with the GalaEvents lablet to see the events it generates and how to write code that handles these events. Explore the code of GalaEvents to see what is involved in event handling. Run and experiment with the SketchPad appelet. Change SketchPad so that it responde differently to some events. Extend SketchPad so that it recognizes and responds to some new events. Exercises You probably remember the Gigobite applet from Chapter 3. It used most of the AWT widgets, all laid out on an somewhat jarring yellow background, like this: Our new new class, GalaEvents, is a subclass of Gigobite, so it not only has access to all of the (non-private) data and methods of Gigobite, but also includes some code to respond to user interaction with the parent class's widgets, reporting when an event occured, which component was involved, and, in some cases, which part or item in the component was selected. Essentially, you can look at the GalaEvents subclass as a watcher over everything that the user might reasonably be expected to do to with Gigobite lablet. Run the GalaEvents lablet. Depending on your environment, you might have to open a "console," "standard output," or "Java messages" window. Ask your instructor or consult your Java system's documentation to find out how to get the program's output to appear. Once you've gotten things running smoothly, experiment with the program, doing all the clicking, mousing, and typing you can think of. Watch the output; try wierd things that a user might not be expected to do. Hint: Keep track of what you're doing and the results of your actions. Take note of anything that's different from what you expect. Advice: Programs--even simple ones like this--are almost always more complicated than you expect them to be. Once you've tried everything you can think of, take a break and then try at least as much as you've already done. For each of the events listed below, describe what you did to make that event occur. Be specific in your description. To see which user activities trigger specific events, look at the documentation of the Event classes. [pp. 249-253] mouseClicked mouseEntered mouseEntered mouseExited mousePressed mouseReleased mouseDragged mouseEntered keyTyped itemStateChanged actionPerformed Look at the source code of GalaEvents. Every event that is handled by a program is generated by some Component and is monitored by some Listener that belongs to another Component, perhaps the same one that generated the event or perhaps a different one. You register a handler Component to deal with events of type XXX from a source Component by using statements of the form source.addXXXListener(handler); as we do in the GalaEvents constructor. In GalaEvents, all events are handled by one Component. Which one? Answer: For each of the event types below (i.e., the XXX part of the addXXXListener() method call), give the Component or Components that are registered as sources. Action Item Key Mouse MouseMotion The first rule of event handling is if a Component isn't registered as a source of an event, none of the events of that type coming from the Component will be processed. Notice, for instance, that none of the lists (like sandwiches) are registered as sources of MouseMotionEvents. Try it--place the pointer in one of the lists and wiggle it around. Notice that none of the motion is detected. Find another event that could come out of one of the Components but isn't reported because the Component hasn't been registered as a source for that type of event. Answer: The second fundamental rule of event handling is if there's no handler for an event, that event won't be processed. We make use of this rule with the VERBOSE constant, to turn off mouse event reporting. Try it--change the declaration private final boolean VERBOSE = true; to private final boolean VERBOSE = false; and compile and run the applet. Notice that the mouse events are no longer reported, since they no longer have a handler. Even if a handler is registered for events from a source Component, the event won't be processed unless the handler has some code to deal with events from the source. To see what we mean, comment out the lines
else if (source == drinks)
{
message += "Drink";
}
from the itemStateChanged() method. We've registered the drinks List as a source of ItemEvents, and registered the applet as a handler for these events. We've also implemented the ItemListener method itemStateChanged(). In making the change above, though, we've removed the code that deals with events that come from the drinks List. What gets reported when you select a drink? Why? Answer: It's important to realize that the event listeners are interfaces [pp. 229-231]. An interface is somewhat like a class, except that when you indicate that a class (like GalaEvents) implements an interface, (like MouseListener) it must have overrides for all the interface's methods, not just the ones you need. Comment out the entire mouseClicked() method of the MouseListener interface. You'll get a strange-looking error message. In your own words, what is the error message telling you? Answer: Advice: Think of implementing an interface as a promise that you'll provide definitions of every method in the interface. As usual, we'll modify GalaEvents now to produce some common errors and simple changes in behavior. In the field below each change, describe the error or behavior change you noted and explain why the error or change was produced. In the changes below, several have to do with the syntax and semantics of the if statement, and most of the rest are concerned with event handling. Why do we need java.io in this applet? To find out, comment out the statement import java.io.*; Result: Change the first line of the header of the GalaEvents class from public class GalaEvents extends Gigobite to public class GalaEvents //extends Gigobite Result: Still in the header of class GalaEvents, comment out the line KeyListener, Result: Go to the actionPerformed() method and remove the { and } braces surrounding the statement System.out.println("order button clicked"); Result: In the actionPerformed() method, change the line else if (source == sandwiches) to read else if (source = sandwiches) Result: In the actionPerformed() method, change the line if (source == order) to read if source == order (That is, remove the parentheses.) Result: Change the line that reads public void mouseEntered(MouseEvent e) to read private void mouseEntered(MouseEvent e) Result: In the GalaEvents constructor, comment out the line superSize.addItemListener(this); Result: Now let's make some fairly direct modifications and extensions to GalaEvents to further explore how events are generated and handled. As you can see from the source, we've made the GalaEvents applet the source of all the mouse events. Run the applet again (with VERBOSE set to true) and watch for MOUSE_ENTERED and MOUSE_EXITED messages as you move the mouse pointer around the applet's frame. When you get a MOUSE_ENTERED message, where is the mouse entering from? It seems as if the order button, for example, "overlays" the applet to which it belongs, intercepting any mouse movement that might have started on the applet's background. Modify GalaEvents so that the order button can be a source of MOUSE_ENTERED mouse events. You'll have to modify the mouseEntered() method so that it checks the source of the events and displays a different message for each source. Our electronic order form isn't as good at sales as the humans you see at the counter. To improve the behavior of our applet, modify it so that whenever a sandwich item is selected, the message "Fries with that?" appears in the reminder textfield. Expand part (b) so that the applet only asks the user about fries if the "Fries" item in the sides list hasn't already been selected. Hint: You might find the List method getSelectedIndex() [p. 89] useful here. The second of this chapter's lablets is SketchPad. This applet is a simple drawing program that allows the user to sketch a picture using a combination of different colors. Drawing is accomplished by dragging the mouse or, for more precision, by using the arrow keys on the keyboard (assuming, that is, that you're using a keyboard that has arrow keys). As usual, we'll start by running the lablet. Draw something; feel free to give your creativity full rein. Use both the mouse and the arrow keys, and make sure you select several colors. Freehand drawing is accomplished by listening for mouseDragged events. These events aren't reported instantaneously; there's a slight delay before a drag event gets posted. To see this, try using the mouse to draw an oval as quickly as you can and then try to trace over the oval slowly and carefully. See if you notice any difference in the look of the two curves. Now look at the source code of GalaEvents. There are five categories of events that need to be handled in this applet. What are they, and how are they generated by the user? Answer: Three of these event types are handled by the applet itself. Which are they? Answer: Which event is handled by a conventional class, external to the SketchPad class? Answer: Finally, which event is handled by a member class, defined inside SketchPad? Answer: Take a look at the .class files produced by the compiler. By looking at the names of the files, how can you tell which were produced from ordinary external classes and which came from internal member classes? Answer: It's experiment time. Make the following changes, one at a time, and predict the result, observe what happens when you compile and run the program, and explain what you observe. In the applet's init() method, comment out the line currentColor = Color.black; Result: Also in the applet's init() method, change the line that reads this.addKeyListener(this); to addKeyListener(this); Result: Comment out the entire update() method. Result: In the applet's paint() method, comment out the line startPoint.setLocation(endPoint); Result: In order for an object to generate a key event, the object must have focus, as would happen, for example, if you clicked in a textfield to edit or enter new text. In the actionPerformed() method, comment out the line requestFocus(); Result: In the keyPressed() method, change the line case KeyEvent.VK_LEFT: so that it reads case KeyEvent.VK_DELETE: Result: Move the code in the body of the keyPressed() method, placing it instead in the keyTyped() method. Result: Refer to the documentation on CheckboxGroup [pp. 84-85]. Then remove the line from the init() method that reads CheckboxGroup drawingColors = new CheckboxGroup; and for each of the four Checkbox declarations following, remove the parameter drawingColors, by, for example, changing color1 = new Checkbox("Red", drawingColors, false); to color1 = new Checkbox("Red", false); Result: There are many natural enhancements we could make to SketchPad. Here are a few of the simpler ones for you to try. Replace the four color checkboxes with a single Choice widget. Allow erasing, by including another color choice that will set the pen color to the color of the background. Instead of having the applet be the listener for key events (and thus requiring us to provide empty implementations for two of its three methods), make a new member class that extends KeyAdapter, in much the same way that we use the Dragger class to handle mouseDragged events. At present, each key press on an arrow key causes the program to draw a 5-pixel-long line. Change the program so that if the Shift key is held down when an arrow key is pressed, a line 10 pixels long will be drawn. Most modern programs give the user the ability to undo the last operation. To do simple undoing in SketchPad, we'll need to remember the last startpoint/endpoint pair so that when the user decides to undo the operation, the last line segment will be redrawn in the background color. Implement an Undo command, activated by a button you'll add to the applet. Have your program work like a real Mac or Windows program by having the Undo command activated by a Control-Z (Command-Z on the Mac) key combination. The Java virtual key constant for the command or control modifier is VK_CONTROL. Postlab Exercises In the interests of not confusing you about events, we did everything we could in SodaPop to hide the event-handling. The result, unfortunately, was a program that was nowhere as clean as it could have been. Go back to SodaPop and make the event handling as tidy as possible. A real drawing program allows the user to draw more than just curves and lines. For example, we might want to give the user the ability to draw ovals and rectangles. Add a choice widget to SketchPad that allows the user to set the drawing mode to produce curves, as it does now, ovals, or rectangles. For example, in oval mode, dragging the mouse and then releasing the mouse button would draw an oval bounded by the rectangle whose opposite corners are determined by the start and end of the dragged curve, like this: You might want to allow two sub-modes for shape drawing, depending on whether the oval or rectange is to be filled or just drawn as an outline. Last updated: December 4, 1998 Rick Decker Department of Computer Science Hamilton College Clinton, NY 13323