![]() |
A6. AWT Programming
In this section, you will learn:
Events 1.0 StyleEvent handling changed drastically between 1.0 and 1.1 versions of Java. Until the runtime world catches up, almost all applet-based programming will require you to create code that uses 1.0 style event handling (and it will work within a 1.1 environment). Until the millions of Netscape Navigator 2.0 and 3.0 users upgrade to a non-existent Java 1.1 version of the browser, your applets should use the 1.0 style event handling, and no features introduced with Java 1.1 (or you need to provide multiple versions of things). Applications that include the Java Runtime Extensions (JRE) are free to do everything with the 1.1-style, since they include the full runtime. After a GUI has built an interface, it waits for an event such as a mouse click or a Button press to trigger an action. All Java Component objects except Label can be triggered into action by an event. When triggered, the Java Component object, including Container objects such as Panels and Applets, can either respond to an event itself or instead let the event be handled by the enclosing Container object, normally called the "parent". (Here, "parent" does not mean super.) Consider the following applet example that overrides mouseDown(): import java.awt.*; import java.applet.Applet; public class SimpleEvent extends Applet { public boolean mouseDown(Event e,int x,int y){ // upon mouse click, put "hi" at that location getGraphics().drawString("hi", x, y); return true; // say that we handled this event } } We have trapped the mouseDown() event in the Applet subclass (which is indirectly a subclass of Container, Component, and Object). Also, notice that if you invalidate the screen it does not retain the previous screen. This is because we are writing to the screen directly with getGraphics() vs. drawing in the paint() routine, or using a separate buffer. Event FlowWhen an event occurs, a call is made to the native window toolkit method
that determines which Component should be signaled with the event. When the appropriate Component is identified, the method Component.postEvent() delivers the event to the Component and invokes handleEvent().
Event Handler Return ValuesEach event handling method returns either true (if the method handled the event) or false (if the method did not handle the event [or did, but you still wants the parent to deal with it]). When handleEvent() returns false, the postEvent() and handleEvent() methods of the parent are called. In this way, non-handled events are passed from innermost to outermost containers. (The applet window is the outermost enclosing container.) Convenience FunctionsMethod handleEvent() calls a number of methods termed convenience functions:
You can customize the response to an event either by overriding the Component.handleEvent() method or overriding one of the convenience functions called by handleEvent(). Although all Component objects will respond to every event they receive, they do not automatically generate all events, in all environments. For example, TextField objects do not generate got/lost focus events on Microsoft Windows and TextArea objects do not generate key down events under Unix. This either requires you to program to the least common denominator or lose functionality when moving between run-time environments. For Internet-based projects, the least common denominator approach seems to be the best way to go. For intranet-based efforts, one can control the interface somewhat and program to that run-time specifically. Action EventsActivating a graphical widget object such as a Button generally triggers the method public boolean action(Event e, Object arg) for that Component. By default, event handlers for components return false. You must subclass the components to override action(). The Event object passed to action() has, among other things, a target (specifying which Component was activated) and an arg field (passed as the second argument). The event argument differs from target to target. For example, the following applet responds to a Button press by drawing a string in the applet window. import java.awt.*; import java.applet.Applet; public class SimpleButtonEvent extends Applet { Button b; public void init() { b = new Button("Press me"); add(b); } public boolean action(Event e, Object arg) { // if the target of the event was our Button if ( e.target == b ) { getGraphics().drawString("OUCH",20,20); return true; // we've handled the event } else { // Didn't handle event, let parent // handle it. Here, there is no parent // because the applet is the root window return false; } } } To further illustrate how events are passed from component to component, consider the following simple applet with two mouseDown event handlers -- one in a Canvas object and one in the applet. import java.awt.*; import java.applet.Applet; // Play a sound upon any mouse click. // If the mouse is in the bordered Canvas region // display "Hi" ALSO. Cascading messages are used // (i.e., the HiRegion responds to mouseDown and by // returning false the applet // responds to the SAME event. public class PassMouseEvent extends Applet { HiRegion region; public void init() { resize(200,200); region = new HiRegion(); add(region); } // upon mouse click, play a sound public boolean mouseDown(Event e, int x, int y) { play(getCodeBase(), "audio/beep.au"); // handled event // don't have parent do anything return true; } } // A square Canvas with a border. // Behavior: puts "Hi" into Canvas for each mouse click. class HiRegion extends Canvas { public HiRegion() { resize(100,100);} public void paint(Graphics g) { // draw a border around canvas g.drawRect(0,0,99,99); } public boolean mouseDown(Event e, int x, int y) { getGraphics().drawString("Hi", x, y); // pretend we did NOT handle this event. return false; // PassMouseEvent.mouseDown() will be called } } The Event ClassWhenever you have an instance of Event, you have access to its many instance variables. When dealing with convenience functions, their values are used as the additional parameters.
Magercises
Events 1.1 StyleWith Java 1.1, instead of the system actively searching for someone to process an event, working its way down and out of the containment hierarchy, objects register as listeners for events. If there are no listeners, nothing happens. If there are twenty listeners registered, each is given an opportunity to process the event, in an undefined order. For example, with a Button, activating the button notifies any registered ActionListeners. Converting the SimpleButtonEvent example from before to 1.1 event handling might look something like this: import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class SimpleButtonEvent11 extends Applet implements ActionListener { Button b; public void init() { b = new Button("Press me"); b.addActionListener (this); add(b); } public void actionPerformed (ActionEvent e) { // if the target of the event was our Button // In this example, check not truly necessary. if ( e.getSource() == b ) { getGraphics().drawString("OUCH",20,20); } } } Notice that there is no boolean value returned and any class can implement ActionListener. All listeners are always notified. If you don't want an event to be processed further, you can call the AWTEvent.consume method. However each listener would then need to check for consumption via the isConsumed method. Consuming events primarily stop events from being processed by the system, after each listener is notified. So, if you want to reject keyboard input from the user, you can consume the KeyEvent. All the KeyListeners will be notified, but the character will not be displayed. (Consumption only works for InputEvents.) So, here is how everything works:
AWTEventAll 1.1 style events, subclass the AWTEvent class. And nearly every event-type has an associated Listener interface, PaintEvent and InputEvent do not. (With PaintEvent, you just override paint and update, for InputEvent, you listen for subclass events, since it is abstract. ![]() Low-level EventsLow-level events represent a low-level input or window operation, like a key press, mouse movement, or window opening. The following table displays the different low-level events, and the operations that generate each event: (each operation corresponds to a method of the listener interface)
For instance, typing the letter 'A' on the keyboard generates three events, one for pressing, one for releasing, and one for typing. Depending upon your interests, you can do something for any of the three events. Semantic EventsSemantic events represent interaction with a GUI component; for instance selecting a button, or changing the text of a text field. Which components generate which events is shown below.
Event SourcesThe following table represents the different event sources, keep in mind the object hierarchy. For instance, when Component is an event source for something, so are all its children:
Notice that although there is only one MouseEvent class, the listeners are spread across two interfaces. This is for performance issues. Since motion mouse events are generated more frequently, if you have no interest in them, you can ignore them more easily, without the performance hit. Event ListenersEach listener interface is paired with one event type and contains a method for each type of event the event class embodies. For instance, the KeyListener contains three methods, one for each type of event that the KeyEvent has, keyPressed, keyReleased, and keyTyped. Summary of Listener interfaces and their methods
Event AdaptersSince the low-level event listeners have multiple methods to implement, there are event adapter classes to ease the pain. Instead of implementing the interface, stubbing out the methods you do not care about, you can subclass the appropriate adapter class, and just override the one or two methods you are interested in. Since the semantic listeners only contain one method to implement, there is no need for adapter classes. public class MyKeyAdapter extends KeyAdapter { public void keyTyped (KeyEvent e) { System.out.println ("User typed: " + KeyEvent.getKeyText(e.getKeyCode())); } } Button Pressing ExampleThe following code demonstrates the basic concept a little more beyond the earlier example. There are three buttons within a Frame, their displayed labels may be internationalized so you need to preserve their purpose within a command associated with the button. Based upon which button is pressed, different actions occur. ![]() import java.awt.*; import java.awt.event.*; public class Activator { public static void main (String args[]) { Button b; ActionListener al = new MyActionListener(); Frame f = new Frame ("Hello Java"); f.add (b=new Button ("Hola"), "North"); b.setActionCommand ("Hello"); b.addActionListener (al); f.add (b=new Button ("Aloha"), "Center"); b.addActionListener (al); f.add (b=new Button ("Adios"), "South"); b.setActionCommand ("Quit"); b.addActionListener (al); f.pack(); f.show(); } } class MyActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { // Action Command is not necessarily label String s = e.getActionCommand (); if (s.equals ("Quit")) { System.exit (0); else if (s.equals ("Hello")) System.out.println ("Bon Jour"); else System.out.println (s + " selected"); } } Since this is an application, you need to save the source, compile it, and run it outside the browser. Adapters ExampleThe following code demonstrates using an adapter as an anonymous inner class to draw a rectangle within an applet. The mouse press signifies the top left corner to draw, with the mouse release the bottom right. import java.awt.*; import java.awt.event.*; public class Draw extends java.applet.Applet { public void init () { addMouseListener(new MouseAdapter() { int savedX, savedY; public void mousePressed(MouseEvent e) { savedX = e.getX(); savedY = e.getY(); } public void mouseReleased(MouseEvent e) { Graphics g = Draw.this.getGraphics(); g.drawRect (savedX, savedY, e.getX()-savedX, e.getY()-savedY); } }); } } If you happen to be running a 1.1 browser, you can try this out. Otherwise, you can save the source and try it with appletviewer. GUI-Based ApplicationsTo create a window for your application, define a subclass of Frame (a Window with a title, menubar, and border) and have the main method construct an instance of that class. For example, import java.awt.*; // Having only the initialization in the // main class allows you to reuse // YourMainWindow more easily in other // situations; YourMainWindow does not have to be // the main program to be reused. public class BasicApplication { public static void main(String[] args) { YourMainWindow w = new YourMainWindow(); w.show(); } } class YourMainWindow extends Frame { public YourMainWindow() { super("YourMainWindow Title"); resize(200,200); // add what you want to this Frame add("Center", new Label("Application Template...")); } } Your applications respond to events in the same way as your applets do. The BasicApplication example above can be modified to respond to the native window toolkit "quit" menu selection (as opposed to a Java menu item on a MenuBar): // handle the WINDOW events here public boolean handleEvent(Event e) { if( e.id == Event.WINDOW_DESTROY ) { hide(); // hide the Frame // tell windowing system that resources can be freed dispose(); System.exit(0); // exit } // anything else, we use the default behavior: // Frame.handleEvent() return super.handleEvent(e); } Consider an application that displays the x,y location of the last mouse click and provides a button to reset the displayed x,y coordinates to 0,0. import java.awt.*; public class BasicEventApplication { public static void main(String[] args) { YourMainWindow2 w = new YourMainWindow2(); w.show(); } } class YourMainWindow2 extends Frame { TextField a,b; Button but; public YourMainWindow2() { super("YourMainWindow Title"); resize(400,200); setLayout(new FlowLayout()); // for example, we add a few text fields // and a button add(new Label("Click the mouse...")); a = new TextField("0", 4); b = new TextField("0", 4); but = new Button("RESET"); add(a); add(b); add(but); } // handle the WINDOW (and Scrollbar) events here public boolean handleEvent(Event e) { if ( e.id == Event.WINDOW_DESTROY ) { hide(); // hide the Frame // tell windowing system to free resources dispose(); System.exit(0); // exit return true; } // anything else, we use the default behavior: // Frame.handleEvent() return super.handleEvent(e); } // handle mouse events like you normally would public boolean mouseDown(Event e, int x, int y) { a.setText(String.valueOf(x)); b.setText(String.valueOf(y)); return true; } // handle buttons (and menus when you add them) // in action method public boolean action(Event e, Object arg) { if ( e.target == but ) { a.setText("0"); b.setText("0"); return true; } return false; } } Applications: Dialog BoxesA Dialog is a window that requires input from the user. Components may be added to the Dialog like any other container. Like a Frame, a Dialog is initially invisible. You must call method show() to activate the dialog box. import java.awt.*; public class DialogTest { public static void main(String[] args) { AMainWindow mw = new AMainWindow(); mw.show(); } } class AMainWindow extends Frame { Dialog d; public AMainWindow() { super("YourMainWindow Title"); resize(200,200); add("Center", new Label ("Dialog test...hit a key")); // make dialog box with label inside and button // last parameter of true means 'modal' d = new Dialog(this, "Error Dialog", false); d.setLayout(new GridLayout(2,1)); d.resize(200,200); d.add(new Label("something BAD happened!")); d.add(new Button("OK")); } // a key press triggers the dialog box public boolean keyDown(Event e, int key) { d.show(); return true; } // handle the WINDOW (and Scrollbar) events here public boolean handleEvent(Event e) { if ( e.id == Event.WINDOW_DESTROY ) { hide(); // hide the Frame // tell windowing system to free resources dispose(); System.exit(0); // exit return true; } // anything else, we use the default behavior: // Frame.handleEvent() return super.handleEvent(e); } } Applications: File Dialog BoxesThe utility class FileDialog is useful for getting file names from the application user. FileDialog.getFile() reports the name of the file selected by the user. If this returns null, the user decided to cancel the selection. Currently, usage of FileDialog is limited to applications. Magercise
Applications: MenusA Java application can have a MenuBar object containing Menu objects that are comprised of MenuItem objects. Menu items can be strings, menus, checkboxes, and separators (a line across the menu). To add menus to any Frame or subclass of Frame:
The following code shows a simple example of these steps: MenuBar mb = new MenuBar(); // Step 1 Menu m = new Menu("File"); // Step 2 m.add(new MenuItem("Open")); // Step 3 m.add(new MenuItem("-")); // add a separator m.add(new CheckboxMenuItem("Allow writing")); Menu sub = new Menu("Options..."); // Create a submenu sub.add(new MenuItem("Option 1")); m.add(sub); // add sub to File menu mb.add(m); // Step 4, add File menu to bar setMenuBar(mb); // Step 5, set menu bar of your // Frame subclass We suggest creating a separate subclass of Menu for each menu you wish to create, in order to isolate the menu selection events from other menus and the normal events (such as mouse clicks) for your Frame. When a menu item is selected, Menu.postEvent() is called, which by default tells the parent of the Menu (usually the surrounding Frame) to post the event. However, we want to handle events for menus directly in a Menu subclass rather than letting the event fall through to the event handler for the entire application. You must override the postEvent() method of each Menu subclass to capture menu selection events: // override postEvent. // e.arg is the selected menu item (a String) public boolean postEvent(Event e) { String item = (String) e.arg; if ( item.equals("selection 1") ) { ... return true; } else if ( item.equals("selection 2") ) { ... return true; } // if not handled, use normal behavior else return super.postEvent(e); } The following application builds a menu bar, shown below, that illustrates all menu construction facilities and incorporates our event handling strategy. import java.awt.*; public class MenuTest { public static void main(String[] args) { MainWindow w = new MainWindow(); w.show(); } } // Make a main window with two top-level menus: // File and Help. // Help has a submenu and demonstrates // a few interesting menu items. class MainWindow extends Frame { public MainWindow() { super("MenuTest Window"); resize(200,200); // make a top level File menu FileMenu fileMenu = new FileMenu(this); // make a top level Help menu HelpMenu helpMenu = new HelpMenu(this); // make a menu bar for this frame // and add top level menus File and Menu MenuBar mb = new MenuBar(); mb.add(fileMenu); mb.add(helpMenu); setMenuBar(mb); } public void exit() { hide(); // hide the Frame // tell windowing system to free resources dispose(); System.exit(0); // exit } } // Encapsulate the look and behavior of the File menu class FileMenu extends Menu { MainWindow mw; // who owns us? public FileMenu(MainWindow m) { super("File"); mw = m; add(new MenuItem("Open")); add(new MenuItem("Close")); add(new MenuItem("Exit")); } // respond to the Exit menu choice // This method does not pass unhandled events to the parent public boolean postEvent(Event e) { String item = (String) e.arg; if ( item.equals("Exit") ) mw.exit(); else System.out.println ("Selected FileMenu " + item); return true; } } // Encapsulate the look and behavior of the Help menu class HelpMenu extends Menu { MainWindow mw; // who owns us? public HelpMenu(MainWindow m) { super("Help"); mw = m; add(new MenuItem("Fundamentals")); add(new MenuItem("Advanced")); add(new MenuItem("-")); add(new CheckboxMenuItem ("Have Read The Manual")); add(new CheckboxMenuItem ("Have Not Read The Manual")); // make a Misc sub menu of Help menu Menu subMenu = new Menu("Misc"); subMenu.add(new MenuItem("Help!!!")); subMenu.add(new MenuItem("Why did that happen?")); add(subMenu); } // respond to a few menu items // This method does not pass unhandled events // to the parent public boolean postEvent(Event e) { String item = (String) e.arg; if ( item.equals("Fundamentals") ) System.out.println("Fundamentals"); else if ( item.equals("Help!!!") ) System.out.println("Help!!!"); // etc... return true; } } The following template can be used to begin most applications needing menus. import java.awt.*; import java.io.*; public class ApplicationTemplate { public static void main(String[] args) { YourMainWindow w = new YourMainWindow(); w.show(); } } class YourMainWindow extends Frame { Menu fileMenu; MenuBar mb; public YourMainWindow() { super("YourMainWindow Title"); resize(200,200); // add what you want to this Frame add("Center", new Label ("An Application Template...")); fileMenu = new Menu("File"); fileMenu.add(new MenuItem("Open")); fileMenu.add(new MenuItem("Close")); mb = new MenuBar(); mb.add(fileMenu); setMenuBar(mb); } // handle the WINDOW (and Scrollbar) events here public boolean handleEvent(Event e) { if ( e.id == Event.WINDOW_DESTROY ) { // any window destroy event hide(); // hide the Frame // tell windowing system to free resources dispose(); System.exit(0); // exit return true; } // anything else, we use the default behavior: // Frame.handleEvent() return super.handleEvent(e); } public boolean mouseDown(Event e, int x, int y) { return false; } // can handle menu events in one giant action() // [or subclass Menu] // handle button events here too.. public boolean action(Event e, Object arg) { if ( e.target instanceof MenuItem ) { System.out.println ("Selected MenuItem " + arg); return true; } return false; } } The following template can be used for version 1.1 applications needing menus. import java.awt.*; import java.awt.event.*; public class ApplicationTemplate11 { public static void main(String[] args) { YourMainWindow11 w = new YourMainWindow11(); w.show(); } } class YourMainWindow11 extends Frame { Menu fileMenu; MenuBar mb; class MyMenuItem extends MenuItem implements ActionListener { MyMenuItem () { addActionListener (this); } MyMenuItem (String s) { super (s); addActionListener (this); } // can handle menu item events in one place public void actionPerformed (ActionEvent e) { System.out.println ("Selected MenuItem " + e.getActionCommand()); } } public YourMainWindow11() { super("YourMainWindow Title"); setSize(200,200); // add what you want to this Frame add(new Label("An Application Template..."), BorderLayout.CENTER); fileMenu = new Menu("File"); fileMenu.add(new MyMenuItem("Open")); fileMenu.add(new MyMenuItem("Close")); mb = new MenuBar(); mb.add(fileMenu); setMenuBar(mb); // handle the WINDOW closing event here addWindowListener (new WindowAdapter() { public void windowClosing (WindowEvent e) { // any window destroy event setVisible(false); // hide the Frame // tell windowing system to free resources dispose(); System.exit(0); // exit } }); } } Magercise
Menu ShortcutsOne nice new feature of Java 1.1 is the ability to provide menu shortcuts or speed keys. For instance, in most applications that provide printing capabilities, pressing Ctrl-P initiates the printing process. In Java 1.0 applications, you could implement this in a roundabout way, but the menu wouldn't display the shortcut properly. Now, when you create a MenuItem, you can specify the shortcut associated with it. If the user happens to press the speed key, the action event is triggered for the menu item. If you want to create two menu items with speed keys, Ctrl-P for Print and Shift-Ctrl-P for Print Preview, the following code would do that: file.add (mi = new MenuItem ("Print", new MenuShortcut('p'))); file.add (mi = new MenuItem ("Print Preview", new MenuShortcut('p', true)));The example above uses Ctrl-P and Shift-Ctrl-P shortcuts on Windows/Motif. The use of Ctrl for the shortcut key is defined by the Toolkit method getMenuShortcutKeyMask. Once there is a Java 1.1 version for the Macintosh, this would be the Command key. An optional boolean parameter to the constructor determines the need for the Shift key. Popup MenusOne restriction of the Menu class is that it can only be added to a Frame. If you want a menu in an Applet, you are out of luck. Although Java 1.1 does not rectify that problem, you can associate a popup-menu with any Component, of which Applet is a subclass. A PopupMenu is similar to a Menu in that it holds MenuItems. However, instead of appearing at the top of a Frame, you pop it up over any component when the user generates the appropriate mouse event. The actual mouse interaction to generate the event is platform specific so Java includes the means to determine if a MouseEvent triggers the popup menu via the MouseEvent.isPopupTrigger method. It is then your responsibility to display the PopupMenu. The following code fragment demonstrates this: public class MyLabel extends Label { ... PopupMenu popup; public void init () { ... popup = new PopupMenu ("Some Title"); popup.add (…) MenuItems add (popup); // Enable mouse event listening, w/o listening // If we add a mouse listener, we don't know what // to listen for and would have to duplicate code. addMouseListener (new MouseAdapter() { public void mousePressed (MouseEvent e) { if (e.isPopupTrigger()) popup.show(e.getComponent(), e.getX(), e.getY()); } }); } } |
Copyright © 1996-1997 MageLang Institute. All Rights Reserved. |