It is common for applications to respond to a key or series of keys and perform some action--for example, Control+q to quit the application. While Flash Player supports all the basic functionality of key combinations from the underlying operating system, it also lets you override or trap any key or combination of keys to perform a custom action.
In some cases, you want to trap keys globally, meaning no matter where the user is in the application, their keystrokes are recognized by the application and the action is performed. Flex recognizes global keyboard events whether the user is hovering over a button or the focus is inside a TextInput control.
A common way to handle global key presses is to create a listener for the KeyboardEvent.KEY_DOWN or KeyboardEvent.KEY_UP event on the application. Listeners on the application container are triggered every time a key is pressed, regardless of where the focus is (as long as the focus is in the application on not in the browser controls or outside of the browser). Inside the handler, you can examine the key code or the character code using the charCode and keyCode properties of the KeyboardEvent class, as the following example shows:
<?xml version="1.0"?>
<!-- events/TrapAllKeys.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();">
<mx:Script><![CDATA[
private function initApp():void {
application.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
}
private function keyHandler(event:KeyboardEvent):void {
t1.text = event.keyCode + "/" + event.charCode;
}
]]></mx:Script>
<mx:TextInput id="myTextInput"/>
<mx:Text id="t1"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
To run this example, you must first set the focus to something inside the application, such as the TextInput control, by clicking on it.
Because any class that extends UIComponent dispatches the keyUp and keyDown events, you can also trap keys pressed when the focus is on an individual component, as in the following example:
<?xml version="1.0"?>
<!-- events/TrapKeysOnTextArea.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();">
<mx:Script><![CDATA[
private function initApp():void {
myTextInput.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
}
private function keyHandler(event:KeyboardEvent):void {
t1.text = event.keyCode + "/" + event.charCode;
}
]]></mx:Script>
<mx:TextInput id="myTextInput"/>
<mx:Text id="t1"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
You can access the keyCode and charCode properties to determine what key was pressed and trigger other actions as a result. The keyCode property is a numeric value that corresponds to the value of a key on the keyboard. The charCode property is the numeric value of that key in the current character set (the default character set is UTF-8, which supports ASCII). The primary difference between the key code and character values is that a key code value represents a particular key on the keyboard (the 1 on a keypad is different than the 1 in the top row, but the 1 on the keyboard and the key that generates the ! are the same key), and the character value represents a particular character (the R and r characters are different).
The mappings between keys and key codes are device and operating system dependent. ASCII values, on the other hand, are available in the ActionScript documentation.
The following example shows the character and key code values for the keys you press. When you run this example, you must be sure to put the focus in the application before beginning.
<?xml version="1.0"?>
<!-- charts/ShowCharAndKeyCodes.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">
<mx:Script><![CDATA[
import flash.events.KeyboardEvent;
private function init():void {
ti1.setFocus();
this.addEventListener(KeyboardEvent.KEY_DOWN, trapKeys);
}
private function trapKeys(e:KeyboardEvent):void {
ta1.text = String(e.toString());
l1.text = numToChar(e.charCode) + " (" + String(e.charCode) + ")";
l2.text = numToChar(e.keyCode) + " (" + String(e.keyCode) + ")";
}
private function numToChar(num:int):String {
if (num > 47 && num < 58) {
var strNums:String = "0123456789";
return strNums.charAt(num - 48);
} else if (num > 64 && num < 91) {
var strCaps:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return strCaps.charAt(num - 65);
} else if (num > 96 && num < 123) {
var strLow:String = "abcdefghijklmnopqrstuvwxyz";
return strLow.charAt(num - 97);
} else {
return num.toString();
}
}
]]></mx:Script>
<mx:TextInput width="50%" id="ti1"/>
<mx:Canvas id="mainCanvas" width="100%" height="100%">
<mx:Form>
<mx:FormItem label="Char (Code)">
<mx:Label id="l1"/>
</mx:FormItem>
<mx:FormItem label="Key (Code)">
<mx:Label id="l2"/>
</mx:FormItem>
<mx:FormItem label="Key Event">
<mx:TextArea id="ta1" width="500" height="200" editable="false"/>
</mx:FormItem>
</mx:Form>
</mx:Canvas>
</mx:Application>
The executing SWF file for the previous example is shown below:
You can listen for specific keys or combinations of keys by using a conditional operator in the KeyboardEvent handler. The following example listens for the combination of the Shift key plus the q key and prompts the user to close the browser window if they press those keys at the same time:
<?xml version="1.0"?>
<!-- events/TrapQKey.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();">
<mx:Script><![CDATA[
private function initApp():void {
application.addEventListener(KeyboardEvent.KEY_UP,keyHandler);
// Set the focus somewhere inside the application.
ta1.setFocus();
}
//This function quits the application if the user presses Shift+Q.
private function keyHandler(event:KeyboardEvent):void {
var bShiftPressed:Boolean = event.shiftKey;
if (bShiftPressed) {
var curKeyCode:int = event.keyCode;
if (curKeyCode == 81) { // 81 is the keycode value for the Q key
/* Quit the application by closing the browser using JavaScript.
This may not work in all browsers. */
var url:URLRequest = new
URLRequest("javascript:window.close()");
navigateToURL(url,"_self");
}
}
}
]]></mx:Script>
<mx:TextArea id="ta1" text="Focus here so that Shift+Q will quit the browser."/>
</mx:Application>
Notice that this application must have focus when you run it in a browser so that the application can capture keyboard events.
If you define keyUp or keyDown event listeners for both a control and its parent, you will notice that the keyboard event is dispatched for each component because the event bubbles. The only difference is that the currentTarget property of the KeyboardEvent object is changed.
In the following example, the application, the my_vbox container, and the my_textinput control all dispatch keyUp events to the keyHandler() event listener function:
<?xml version="1.0"?>
<!-- events/KeyboardEventPrecedence.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();">
<mx:Script><![CDATA[
private function initApp():void {
application.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
my_vbox.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
my_textinput.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
// Set the focus somewhere inside the application.
my_textinput.setFocus();
}
private function keyHandler(event:KeyboardEvent):void {
ta1.text += event.target + "(" + event.currentTarget + "): " +
event.keyCode + "/" + event.charCode + "\n";
}
]]></mx:Script>
<mx:VBox id="my_vbox">
<mx:TextInput id="my_textinput"/>
</mx:VBox>
<mx:TextArea id="ta1" height="300" width="550"/>
</mx:Application>
The executing SWF file for the previous example is shown below:
When you examine the output, you will notice that the target property of the KeyboardEvent object stays the same because it refers to the original dispatcher of the event (in this case, my_textinput). But the currentTarget property changes depending on what the current node is during the bubbling (in this case, it changes from my_textinput to my_vbox to the application itself).
The order of calls to the event listener is determined by the object hierarchy and not the order in which the addEventListener() methods were called. Child controls dispatch events before their parents. In this example, for each key pressed, the TextInput control dispatches the event first, the VBox container next, and finally the application.
When handling a key or key combination that the underlying operating system or browser recognizes, the operating system or browser generally processes the event first. For example, in Microsoft Internet Explorer, pressing Control+w closes the browser window. If you trap that combination in your Flex application, Internet Explorer users never know it, because the browser closes before the ActiveX Flash Player has a chance to react to the event.
The MouseEvent class and all MouseEvent subclasses (such as ChartItemEvent, DragEvent, and LegendMouseEvent) have the following properties that you can use to determine if a specific key was held down when the event occurred:
|
Property |
Description |
|---|---|
|
altKey |
Is set to true if the Alt key was held down when the user pressed the mouse button; otherwise, false. |
|
ctrlKey |
Is set to true if the Control key was held down when the user pressed mouse button; otherwise, false. |
|
shiftKey |
Is set to true if the Shift key was held down when the user pressed mouse button; otherwise, false. |
The following example deletes Button controls, based on whether the user holds down the Shift key while pressing the mouse button:
<?xml version="1.0"?>
<!-- events/DetectingShiftClicks.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();">
<mx:Script><![CDATA[
import mx.controls.Button;
private function initApp():void {
var b1:Button = new Button();
b1.label = "Button 1";
var b2:Button = new Button();
b2.label = "Button 2";
b1.addEventListener(MouseEvent.CLICK, removeButtons);
b2.addEventListener(MouseEvent.CLICK, removeButtons);
vb1.addChild(b1);
vb1.addChild(b2);
}
private function removeButtons(event:MouseEvent):void {
if (event.shiftKey) {
vb1.removeChild(Button(event.currentTarget));
} else {
event.currentTarget.toolTip = "Shift+click to remove this button.";
}
}
]]></mx:Script>
<mx:VBox id="vb1"/>
<mx:Button id="resetButton" label="Reset" click="initApp();"/>
</mx:Application>
The executing SWF file for the previous example is shown below: