700 likes | 1.03k Views
Events. Here, we review event handling one more time. To understand how events work in Java, we have to look closely at how we use GUIs. When you interact with a GUI, there are many events taking place each second. Only a few of these, however, may actually be ‘delivered’ to the application.
E N D
Events Here, we review event handling one more time. To understand how events work in Java, we have to look closely at how we use GUIs. When you interact with a GUI, there are many events taking place each second. Only a few of these, however, may actually be ‘delivered’ to the application.
Events Java uses a “delegation” event model found in many other toolkits. Under the delegation model, components fire events, which can be caught and acted on by listeners. A listener is linked to a component through a registration process. The delegation event model is contrasted to an event filtration model where all events are delivered to target components regardless of whether they asked for them.
General Overview Recall our first consideration of events, where our first frame would not close, even when the end of main() was reached. We explained this behavior by thinking of our program as entering an “infinite loop” when the graphics are shown. This ‘infinite loop’ is actually an event-driven cycle, but we can think of it as a “while (true)” structure that periodically polls for user input.
The Real Story import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI We usually think of our program as a single, linear set of steps being executed. But something special happens when we create graphical objects.
The Real Story import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI When Java sees that you’ve created a GUI, your program gets a second “set” of linear instructions. This is actually a separate “thread”, but don’t worry if that’s unclear for now. We can think of this as a second part of our program than handles special graphics-related tasks (such as drawing the window, etc.)
Graphics Thread import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI Both of these “threads” are your program. You coded one of the lines of control. Java provides the other one. That way, things appear to happen simultaneously--your code executes and the window gets redrawn, refreshed, etc. Java quickly switches between your code and the graphics drawing code, so that both threads appear to execute at the same time.
Don’t Panic import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI Don’t worry if this “thread” stuff is confusing. Other classes go into this in detail. For our purposes, we only have to understand that there are two areas of a graphic program. 1 The code we wrote in main() and other methods Your Code Graphics The code Java provides to handle the graphics side of things. 2
Who Cares? import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI This model is very important to understand because as it turns out, when an event occurs--such as mouse click, it happens in the “graphics side” of the model. Mouse Click occurs The code trapping this event appears in the graphics thread Actually, there’s a separate “event queue” that handles incoming events. But this is already complicated enough. Let’s just generalize and imagine that all events arrive in the ‘graphics side’ of things.
“Call backs” import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI Since the event arrived in the ‘graphics half’ of our program, we need a way to have it call a method in our program. This is known as a “call back”. The code trapping this event appears in the graphics thread Our event handling code callback
How? import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI callback So Java needs to call some event handling code that we write. The trouble is, how will Java know what we called out method? We can name them anything we want, and Java won’t necessarily know what methods handle events. But Wait! We can use interfaces, right?
Event Interfaces ActionListener import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI callback Java uses interfaces as its primary event handling scheme. If you implement an event-related interface, Java will know which methods to call. This is because the contract nature of interfaces requires all methods to appear in the implementing class. public void actionPerformed (ActionEvent e) { // code doing something } This method MUST be there, so Java knows it can “callback” to it
Why “Delegation”? Since any class can implement any interface, we can have just about any object handle the events. We can therefore “delegate” event handling to this object. Remember MVC? Some (smart) folks believe you should organize where the events get handled. (This is the “controller” aspect to MVC.) M V C
Why “Registration”? import java.awt.*; public class HelloGUI { public static void main (String[ ] arg) { System.out.println (“About to make GUI”); Frame f = new Frame (“Hello GUIs”); f.setSize( 200, 200 ); f.show(); System.out.println (“Finished making GUI”); }// main }// class HelloGUI We are told that “event registration” must occur before event handling will occur. What does this mean? Well, since we can have any class handle events, we need to tell Java which object implements the proper event handling interface. This “registers” the component as being interested in receiving callbacks. Where to callback?
Another example public class DemoFrame extends Frame { public DemoFrame( ) { super (“A poor use of inheritance, but simple”); Handler2 h = new Handler2(); this.setSize(400,400); this.setLayout(new FlowLayout()); Button b = new Button (“Click me”); this.add(b); this.show(); } // Constructor public static void main(String args[]) { DemoFrame df; df = new DemoFrame(); } // main } // DemoFrame
Another example public class Handler2 implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println (“Button was clicked”); } } // Handler2 Why doesn’t this work?
Another example public class DemoFrame extends Frame { public DemoFrame( ) { super (“A poor use of inheritance, but simple”); Handler2 h = new Handler2(); this.setSize(400,400); this.setLayout(new FlowLayout()); Button b = new Button (“Click me”); b.addActionListener(h); this.add(b); this.show(); } // Constructor public static void main(String args[]) { DemoFrame df; df = new DemoFrame(); } // main } // DemoFrame
Question... We said we had to have a Listener to handle the event and it had to be an object. Does it have to be a separate object?
Another example public class DemoFrame extends Frame implements ActionListener { public DemoFrame( ) { super (“A poor use of inheritance, but simple”); Handler2 h = new Handler2(); this.setSize(400,400); this.setLayout(new FlowLayout()); Button b = new Button (“Click me”); b.addActionListener(this); this.add(b); this.show(); } // Constructor public void actionPerformed(ActionEvent e) { System.out.println (“Button was clicked”); } public static void main(String args[]) { DemoFrame df; df = new DemoFrame(); } // main } // DemoFrame
BSOD This program has performed an illegal instruction and will be shutdown. Please shell out another $200 for a more stable version of this OS.
Just Kidding a Mouse events b Keyboard events c Timing events d Other user action inputs Anything can be an event. Including general protection faults. But for the most part, good programming dictates that handled events should come from the following area of input:
Java Event Handling Strategies Very similar 1 Event Listeners 2 Event Adapters Very general 3 Semantic Events Very old 4 Inheritance-based event handling With this basic understanding, we can investigate the FOUR primary means of event handling in Java You are 100% guaranteed to have a quiz question on this
Listeners Strategy No. 1 From the discussion about callbacks, we noted that interfaces were the primary mechanism for structuring our event handling. There are numerous event interfaces we can implement, roughly divided around categories of events. The next slide lists many of them. Don’t freak out because there are so many. We’ll highlight the most commonly used ones. . .
Yikes. So Many Choices Most commonly used • Package java.awt.event features: • ActionListener • MouseListener • MouseMotionListener • AdjustmentListener • ComponentListener • FocusListener • ContainerListener • ItemListener • KeyListener • WindowListener • TextListener As it turns out, the ActionListener is part of the “semantic” event group, even though it’s an interface. So let’s focus on simple events like MouseListener...
MouseListener The MouseListener interface has several methods we have to code: public void mouseClicked(MouseEvent e) { } -- a timing-based determination; else the events are processed as pressed/releases public void mouseEntered(MouseEvent e) { } -- entry into component public void mouseExited(MouseEvent e) { } -- exit from component public void mousePressed(MouseEvent e) { } -- simply a press . . . public void mouseReleased(MouseEvent e){ } -- ... the corresponding release
import java.awt.*; import java.awt.event.*; public class MouseFrame implements MouseListener{ Color highlight, normal; boolean bHighlight = true; Frame fr; public MouseFrame () { fr = new Frame(“For demonstration only”); highlight = Color.red; normal = Color.gray; frame.setSize(400,400); Button b = new Button("Click"); b.addMouseListener(this); fr.setBackground(normal); fr.setLayout(new FlowLayout()); fr.add(b); fr.show(); } public static void main(String[] args) { new MouseFrame(); } To keep it simple, we ignore WindowEvents Note that when we run this the constructor will run and terminate (more)
public void mouseReleased(MouseEvent e){ System.out.println ("Changing color"); if (bHighlight) frame.setBackground(highlight); else frame.setBackground(normal); bHighlight = !bHighlight; } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} } // MouseFrame “click” “click”
Event Listener Summary We need a class that implements the appropriate listener type. We need to “register” a component as interested in receiving events: addXYZListener ( <listener instance> ); Whatever listener we’re working with. E.g.: addMouseListener(this); addMouseMotionListener(myEventHandler);
Observations 1 The “WindowListener” interface required numerous methods. But only one was important to us. All the rest were coded as “no-op” or no operation methods. 2 3 There’s another strategy using “adapters”, using inheritance that could have saved us some trouble...
Adapters Strategy No. 2 Java has built-in classes called “event adapters” that implement each of the various event listeners. But all of these methods are “no-ops”. public class MouseAdapter implements MouseListener { public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} } WHY?
Key to Adapters: Inheritance MouseAdapter MouseFrame Why a bunch of no-op methods? Well, if you subclass the adapter, your class IS-A type of event listener. And you then only have to override the one or two methods you care about. The rest can be inherited as “no-ops”
import java.awt.*; import java.awt.event.*; public class MouseFrame extends MouseAdapter implements MouseListener{ Color highlight, normal; boolean bHighlight = true; Frame frame; public MouseFrame () { frame = new Frame(“For demonstration only”); highlight = Color.red; normal = Color.gray; frame.setSize(400,400); Button b = new Button("Click"); b.addMouseListener(this); frame.setBackground(normal); frame.setLayout(new FlowLayout()); frame.add(b); frame.show(); } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} Parent class takes care of these
Example (cont’d) public void mouseReleased(MouseEvent e){ System.out.println ("Changing color"); if (bHighlight) frame.setBackground(highlight); else frame.setBackground(normal); bHighlight = !bHighlight; } public static void main(String[] args) { new MouseFrame(); } } // MouseFrame We override the one or two methods we care about Same behavior; less code; but we use up our single inheritance
public class MouseAdapter implements MouseListener { public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} } This comes with Java! import java.awt.*; import java.awt.event.*; public class MouseFrame extends MouseAdapter { Color highlight, normal; boolean bHighlight = true; Frame frame; public MouseFrame () { frame = new Frame(“For demonstration only”); highlight = Color.red; normal = Color.gray; frame.setSize(400,400); Button b = new Button("Click"); b.addMouseListener(this); frame.setBackground(normal); frame.setLayout(new FlowLayout()); frame.add(b); frame.show(); } public void mouseReleased(MouseEvent e) { System.out.println ("Changing color"); if (bHighlight) frame.setBackground(highlight); else frame.setBackground(normal); bHighlight = !bHighlight; } public static void main(String[] args) { new MouseFrame(); } } // MouseFrame
Big Picture Time So far, we’ve tinkered(kurcalamak) with different ways of coding very low-level event handling. But what if our event handling needs are very general. Consider this simple dialog box: There’s not much interaction that needs to be supported. Mouse entry/exit might not be needed at all. Are you sure you wish to proceed ? ok cancel
Semantic Events Wouldn’t it be convenient to abstract all of these small events into one “just-tell-me-when-its-clicked” event? public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} M1A1 Abstractor public void actionPerformed(ActionEvent e)
Semantic Events Strategy No. 3 Semantic events provide a means of handling events at the component level. That is, you will not address fine-grained events like mouse entry and exit. Instead, you’ll only receive a callback when the component has received some type of input event
Semantic Events Semantic Event Components and Firing Event ActionEvent Button (activated) List (double-clicked) There are numerous event handlers for low-level events associated with these widgets. MenuItem (selected) TextField (typed) AdjustmentEvent Scrollbar (moved) ItemEvent Checkbox (toggled) CheckboxMenuItem (selected) Choice (selected) List (selected) TextEvent TextComponent (text changes) Note: ALL input is sent into one of these FOUR categories.
Example import java.awt.*; import java.awt.event.*; public class MouseFrame extends Frame implements ActionListener { Color highlight, normal; boolean bHighlight = true; public MouseFrame () { highlight = Color.red; normal = Color.gray; this.setSize(400,400); Button b = new Button("Click"); b.addActionListener(this); this.setBackground(normal); this.setLayout(new FlowLayout()); this.add(b); this.show(); }
Example (cont’d) public void actionPerformed(ActionEvent e){ System.out.println ("Changing color"); if (bHighlight) this.setBackground(highlight); else this.setBackground(normal); bHighlight = !bHighlight; } public static void main(String[] args) { new MouseFrame(); } } // MouseFrame We therefore lose the ability to handle very fine-grained events (e.g., mouse entry/exit). But that might be acceptable for certain applications.
Strategy No. 4 (non) Option #4: JDK 1.02 Events • An earlier version of Java used “boolean” return values to indicate consumption of events. Events were delivered to components whether or not they registered for events. • Not recommended; still used for some web development • Do not mix JDK 1.1 and JDK 1.02 event handlers--the component ceases to function! • Rare use: JDK 1.02 guarantees which event will arrive first to a component. • More common use: some browsers only support JDK 1.02--a very early version of Java that uses this model. Professional applet developers still use this technique. Many browsers are now supporting the JDK 1.1 event model.
Event Handling Options: How to Decide Costs Benefits Event Listeners (interfaces) Event Adapters (inheritance) Semantic Events Must code all methods; wasteful no-ops result Keep all events in single class Uses up single inheritance opportunity Good abstraction; override those methods you need Simplifies event handling Loss of granular control; linear code
Debuggingre: Event Handlers • Debugging an event-driven program (whether applet or graphical application) is more tricky than debugging a non-event-driven program. • With an event-driven Java program, you don't explicitly code any kind of event-handling loop that "polls" for occurring events, then calls the appropriate handler(s) for those events. • Instead, the Java internals handle this polling action for you. Debugging becomes trickier because now you have to make sure that your event handling code works correctly. • You also have to make sure you're handling the correct events in the first place! For example, your code for mouseEntered( ) may work perfectly, but if you're expecting it to get called when the user clicks a mouse button, it won't be!
Debugging re: Event Handlers • So, in debugging event-driven programs written • with Java, the steps are: • Be sure you're handling the appropriate events: • Map out on paper what events get thrown from what components, and what class(es) handle them. • Handle the events appropriately: This is the kind of debugging you're already familiar with: Once you're sure the appropriate events are getting handled, the rest is being sure the event-handling code (and the code that the event handlers call) work. System.out.println is still your friend...
My Program Events: A Short Example To compare the three event handling techniques, let’s see a *brief* example how all three might work on a common problem. Goal: Create a simple Frame that holds a TextArea and Button. The Button toggles the ability to edit the TextArea The Panel holding the Button and TextArea is placed in a Frame subclass, which handles its own disposal TEXT AREA BUTTON Panel subclass
import java.awt.*; import java.awt.event.*; public class MyFrame extends Frame implements WindowListener{ public static final int iWidth = 300, iHeight = 500; public MyFrame() { this.setSize(iWidth, iHeight); this.addWindowListener(this); BorderLayout border = new BorderLayout(); this.setLayout(border); } public void windowClosing (WindowEvent e) { e.getWindow().setVisible(false); e.getWindow().dispose(); System.exit(0); } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }// class MyFrame Constructor WindowListener Frames are not self-disposing! (Setting Frame invisible first eliminate flicker.)
import java.awt.*; import java.awt.event.*; public class MyFrame extends Frame { public static final int iWidth = 300, iHeight = 500; public MyFrame() { this.setSize(iWidth, iHeight); this.addWindowListener (new WindowAdapter() { public void windowClosing (WindowEvent e) { e.getWindow().setVisible(false); e.getWindow().dispose(); System.exit(0); } }); BorderLayout border = new BorderLayout(); this.setLayout(border); } }// class MyFrame “Anonymous Inner Class”: used as a short cut. For your code, use listeners Frames are not self-disposing! (Setting Frame invisible first eliminate flicker.)
import java.awt.*; public class Driver { public static void main (String arg[]){ Notepad note = new Notepad(); MyFrame f = new MyFrame(); f.add(note, BorderLayout.CENTER); f.show(); }//main }//class Driver A simple driver. Notice that so far, we’ve abstracted the Frame subclass into something very generic and reusable--IT’S NOT JUST TIED TO THIS PROGRAM!
Variation #1: Listener Events (MouseListener) import java.awt.*; import java.awt.event.*; class Notepad extends Panel implements MouseListener { Button toggle; TextArea scratch; boolean bWritable; public Notepad() { super("Wasted inheritance"); this.setLayout (new BorderLayout()); scratch = new TextArea(20,20); Panel buttonPanel = new Panel(); toggle = new Button ("Freeze/Unfreeze"); buttonPanel.add(toggle); add(scratch, BorderLayout.CENTER); add(buttonPanel, BorderLayout.SOUTH); toggle.addMouseListener(this); bWritable = false; }// constructor The Driver and MyFrame classes were generic enough to work with any version of this example. Here, however, we need to create a specific event handler.
/* . . . Continued from “class Notepad extends Panel implements MouseListener” . . . */ public void setWritable(boolean bWritable){ this.bWritable = bWritable; }//setWritable public boolean getWritable() { return bWritable; }//getWritable public TextArea getTextArea(){ return scratch; }//getTextArea public void mousePressed(MouseEvent e) { getTextArea().setEnabled(getWritable()); setWritable(!getWritable()); }//mousePressed public void mouseReleased(MouseEvent e) {;} public void mouseClicked(MouseEvent e) {;} public void mouseEntered(MouseEvent e) {;} public void mouseExited(MouseEvent e) {;} }//class Notepad Implement the method one needs; the rest are “no-ops”