In the last tutorial you've learned how to create and access pop- up menus in AIR applications with Flash CS4.
The menus you’ve created in that examples so far have all been generated through predefined ActionScript code. If you wanted to change the label of an item on the menu, change the order of the items, or remove or add elements, you would have to recompile the Flash project. You would also have to generate a new installation file for the application, or provide an update, if the application supports updates.
When you’re developing real applications, it’s advisable to create applications that don’t need to be recompiled every time you make minor changes. If your user had to update the whole application (not just a few configuration files) every week or every month, you’d probably lose your audience quite quickly.
To avoid or at least limit these situations, you can extend the NativeMenu class . You can create a class that allows the menu items to be generated dynamically from an XML structure. Because native menus are hierarchal, it’s relatively easy to represent a native menu skeleton to an XML structure. Another advantage is the possibility of changing the localization
of the menu of the application by loading different XML documents.
First, you have to define the structure of the XML document so that each item on the menu is represented by a MenuItem node . The attributes of each node will define the properties to be assigned to the corresponding instance of the NativeMenuItem class that will be created. You can also embed MenuItem nodes in other MenuItem nodes to generate submenus.
A MenuItem node will have the following skeleton:
<MenuItem label="" isSeparator="" mnemonicIndex="" keyEquivalent="" name="" select="" />
The attributes provided allow you to define the structure and behavior of the corresponding menu items. Here is an analysis of the attributes:
label : This is the text label that will be assigned to the element, be it a command
or a submenu.
isSeparator : This is a Boolean value that specifies if the element will be a separator or not. If the value is true, the element will be a separator, so all other attributes will be ignored.
mnemonixIndex : This specifies the index that corresponds to the letter of the text label that is recognized as a shortcut key. You can use this shortcut when you are exploring the items on the menu with the keyboard arrows. This parameter can
only be applied in AIR projects with window and application menus.
keyEquivalent : This specifies the letter that will select an item on the menu when it’s pressed in conjunction with the Ctrl key on Windows or the Cmd key on Mac OS X. In order for this shortcut to work, the application has to be active and in the
foreground. This parameter can only be applied to window and application menus.
name : This is a unique ID that doesn’t depend on the localization of the element.
select : This is the name of the method that has to be called upon a selection
event. This attribute allows you to easily activate, deactivate, or change the functions
Extending the NativeMenu class
Now you can create the class that will dynamically generate native menus for your application.
The class is called XMLMenu . You can find it in the com.comtaste.foed.essentialair.
chapter6 namespace in the src folder of the ch06p06.fla project. The class begins by
declaring its namespace and the classes to import in the compilation phase of the application:
The class is called XMLMenu . You can find it in the com.comtaste.foed.essentialair.
chapter6 namespace in the src folder of the ch06p06.fla project. The class begins by
declaring its namespace and the classes to import in the compilation phase of the application:
package com.comtaste.foed.essentialair.chapter6
{
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.events.Event;
{
import flash.display.NativeMenu;
import flash.display.NativeMenuItem;
import flash.events.Event;
The following class declaration tells Flash that you’re extending the NativeMenu class :
public class XMLMenu extends NativeMenu
{
{
that are associated with an element on the menu.
Then you define the necessary class properties. A property will keep track of the context in which the methods of the select attributes in the MenuItem nodes will be executed. A property will store a copy of the original source XML of the menu.
// execution scope for mapped functions
private var _scope:Object;
// stored xml source
private var _source:XML;
private var _scope:Object;
// stored xml source
private var _source:XML;
The constructor method of the class requires the two following arguments. The first is the XML that defines the structure of the menu, and the second is the scope in which the functions that are associated with the selection events in the XML are executed.
public function XMLMenu( XMLSource:XML, scope:Object )
{
// call super constructor
super();
{
// call super constructor
super();
Then you assign the received XML instance to the class variables, as well as the object that will be the execution scope for the functions you need to map:
// store mapped function scope
_scope = scope;
// store xml source
_source = XMLSource;
_scope = scope;
// store xml source
_source = XMLSource;
Next, you extract an instance of the XMLList class that contains all the top- level MenuItem nodes of the received XML document by using E4X syntax:
// generate xmlList of all top- level <menuItem> xml nodes
var menuItems:XMLList = XMLSource.MenuItem;
var menuItems:XMLList = XMLSource.MenuItem;
Now that everything has been prepared correctly, you call the createItemFromXML() method . You provide the list of elements to create as an argument. You also specify that they need to be added as children of the class itself, as shown here:
// generate menu items from xml
createItemFromXML( this, menuItems );
}
createItemFromXML( this, menuItems );
}
The recursive createItemFromXML() method starts from a list of XML nodes, like the ones that you’ve defined as MenuItem. You generate a native menu in which each element corresponds to an XML node. The first argument of the method requires an instance of the NativeMenu class . Finally, you add the elements you’ve created to this instance from the nodes you’ve provided as a second argument of the function. Here’s the code:
// initialize xml- defined menu items
private function createItemFromXML( menu:NativeMenu,
source:XMLList ):NativeMenu
{
private function createItemFromXML( menu:NativeMenu,
source:XMLList ):NativeMenu
{
Then you define the following instance of the NativeMenuItem class , which you’ll use to generate the items of the native menu in a for...each loop :
// menu item that will be created
var item:NativeMenuItem;
var item:NativeMenuItem;
For each XML node, you access its attributes and use them to populate the properties of the NativeMenuItem object you’re creating. Here’s the code:
// iterate on any top- level node
// and create a submenu for each element
var xmlItem:XML;
for each( xmlItem in source )
{
// access menu item details
// and create a submenu for each element
var xmlItem:XML;
for each( xmlItem in source )
{
// access menu item details
To access the attributes of the node, you used the E4X syntax. The syntax requires the attributes to be accessed by specifying [email protected]. For each MenuItem node ,you access all the properties provided by the XML object. Each attribute has to be saved in the right kind of local variable for the received data. These operations are shown in the
following code:
following code:
// label for item
var itemLabel:String = String( [email protected] );
// item's ID, not displayed
var itemName:String = String( [email protected] );
// mnemonic index key
var itemMnemonicIndex:int = int( [email protected] );
// key equivalent associated with item,
// need COMMAND key on Mac and Control key on Windows
var itemKeyEquivalent:String = String([email protected]);
// indicates if item is a separator
var itemIsSeparator:Boolean = false;
if( String( [email protected] ) == 'true' )
{
itemIsSeparator = true;
}
// callback function name to recall when item is selected
var itemSelectLabel:String = String( [email protected] );
var itemLabel:String = String( [email protected] );
// item's ID, not displayed
var itemName:String = String( [email protected] );
// mnemonic index key
var itemMnemonicIndex:int = int( [email protected] );
// key equivalent associated with item,
// need COMMAND key on Mac and Control key on Windows
var itemKeyEquivalent:String = String([email protected]);
// indicates if item is a separator
var itemIsSeparator:Boolean = false;
if( String( [email protected] ) == 'true' )
{
itemIsSeparator = true;
}
// callback function name to recall when item is selected
var itemSelectLabel:String = String( [email protected] );
The last attribute to check, select, allows you to register an event listener function dynamically for the selection event of a given node. The XML node, via this attribute, specifies the name of the required event listener method, but doesn’t specify where the method actually is. The execution environment of the method is provided to the constructor of the class and stored in the _scope variable . Finally, the getFunction() method checks for the presence of the function in the given execution environment and returns a reference to the event listener function or the null value:
// try to retrieve requested callback on defined scope
var itemSelect:Function = getFunction( itemSelectLabel );
var itemSelect:Function = getFunction( itemSelectLabel );
After having extracted and validated the values assigned to all the attributes of the node in question, you can assign the reference to a new instance of the NativeMenuItem class to the local item variable . This variable is declared outside of the iterative loop. The label defined by the XML node is provided to the constructor method. You also specify whether the instance you’re creating is an item separator in the method. You assign the value of the corresponding attribute to the name property of the NativeMenuItem class . The property won’t be displayed in the menu but will be the unique ID for the element. Here’s the code:
// instantiate menu item
item = new NativeMenuItem( itemLabel, itemIsSeparator );
// assign values to correct properties
item.name = itemName;
item = new NativeMenuItem( itemLabel, itemIsSeparator );
// assign values to correct properties
item.name = itemName;
All the other properties provided by the XML node are only necessary if the item isn’t an item separator, so next, you check the value of the Boolean itemIsSeparator variable before assigning the rest of the information:
// all other properties are needed
// only if menu item is not a separator
if( !itemIsSeparator )
{
// assign given mnemonic index
item.mnemonicIndex = itemMnemonicIndex;
// assign given key equivalent shortcut
item.keyEquivalent = itemKeyEquivalent;
If the itemSelect variable isn’t null, it means that a correspondence has been found in
the relevant execution environment (_scope) for the required event listener method (attribute
select). Here’s an example:
// if available, map callback to select event
if( itemSelect != null )
{
item.addEventListener( Event.SELECT, itemSelect );
}
}
// only if menu item is not a separator
if( !itemIsSeparator )
{
// assign given mnemonic index
item.mnemonicIndex = itemMnemonicIndex;
// assign given key equivalent shortcut
item.keyEquivalent = itemKeyEquivalent;
If the itemSelect variable isn’t null, it means that a correspondence has been found in
the relevant execution environment (_scope) for the required event listener method (attribute
select). Here’s an example:
// if available, map callback to select event
if( itemSelect != null )
{
item.addEventListener( Event.SELECT, itemSelect );
}
}
Once you’ve completed the object of the NativeMenuItem class , you can add it to the menu as an item. Do so by calling the addItem() function of the NativeMenu class, as shown here:
// add menu item created to parent
menu.addItem( item );
menu.addItem( item );
Before creating another menu item, you check if the XML node contains child MenuItem nodes. If so, it means that the element you’ve created will become a submenu of the menu.
Next, you create an XMLList object containing the subnodes. Then you assign an instance of the NativeMenu class to the submenu property of the NativeMenuItem item you’re creating. Finally, you call the createItemFromXML() method , specifying that the list of XML nodes will have to populate the initialized submenu property .
// if menu item is itself a menu
// populate it
var subMenuItems:XMLList = xmlItem.MenuItem;
if( subMenuItems.length() > 0 )
{
// instantiate submenu object
item.submenu = new NativeMenu();
// populate submenu
createItemFromXML( item.submenu, subMenuItems );
}
}
At the end of the for...each loop , you return the populated menu to the calling method,
like this:
// return menu
return menu;
}
The createItemFromXML() method uses the following getFunction() function to check if the event listener method is valid. The value becomes the attribute of the XML node.
// populate it
var subMenuItems:XMLList = xmlItem.MenuItem;
if( subMenuItems.length() > 0 )
{
// instantiate submenu object
item.submenu = new NativeMenu();
// populate submenu
createItemFromXML( item.submenu, subMenuItems );
}
}
At the end of the for...each loop , you return the populated menu to the calling method,
like this:
// return menu
return menu;
}
The createItemFromXML() method uses the following getFunction() function to check if the event listener method is valid. The value becomes the attribute of the XML node.
private function getFunction( name:String ):Function
{
{
The method receives a string, which corresponds to the name of the function, as an argument. You create a local variable of the Function class , specifying that its initial value is null. Before proceeding, you check that the function name received as an argument for the function isn’t null or an empty text string. If it isn’t valid, you interrupt the execution of the function and return the null value to the calling method. Here’s the code:
var f:Function = null;
// if name is not valid do not proceed
if( name == null || name == "" )
return null;
// if name is not valid do not proceed
if( name == null || name == "" )
return null;
Open a try...catch construct . In the construct, you assign the reference to the required method from the local variable you’ve set up for this purpose. Then you obtain a reference to the required method through the literal access to the relevant execution environment.
If the relevant execution method is a dynamic object and the required method doesn’t exist, the variable will keep its null value. If the execution environment is an instance of a class, the attempt to access a nonexistent property will cause an exception in Flash Player. The exception will be intercepted by the try...catch construct, avoiding unwanted error messages during the execution of the application. The following example accomplishes these tasks:
If the relevant execution method is a dynamic object and the required method doesn’t exist, the variable will keep its null value. If the execution environment is an instance of a class, the attempt to access a nonexistent property will cause an exception in Flash Player. The exception will be intercepted by the try...catch construct, avoiding unwanted error messages during the execution of the application. The following example accomplishes these tasks:
// use try...catch to avoid unwanted runtime exceptions
try
{
// try access callback function in defined scope
f = _scope[ name ];
} catch ( e:TypeError )
{
// The callback specified in source file
// is something other than a method
trace('The callback "' + name +
'" is invalid for object type XMLMenu' );
} catch ( e:ReferenceError )
{
// The callback does not exist
trace( 'The callback "' + name +
'" does not exist for object type XMLMenu');
}
// return retrieved function's pointer
return f;
}
try
{
// try access callback function in defined scope
f = _scope[ name ];
} catch ( e:TypeError )
{
// The callback specified in source file
// is something other than a method
trace('The callback "' + name +
'" is invalid for object type XMLMenu' );
} catch ( e:ReferenceError )
{
// The callback does not exist
trace( 'The callback "' + name +
'" does not exist for object type XMLMenu');
}
// return retrieved function's pointer
return f;
}
The XMLMenu class is now ready to be used when you need to generate native menus dynamically in your applications. In the next section, you’ll create an AIR application that will use this class.
OMG CS4, I heard there's already CS5..I'm so late now..I got stuck at CS3..I would like to try this out.
http://cpa-city.com/im-product-review/wp-mage-monster-i-used-it-wp-mage-monster-review
Posted by: Account Deleted | July 08, 2011 at 07:02 PM
Its really wonderful and watchable. I like to share it with all my friends and hope they will like it
My Blog : http://www.flirtwithagirl.org - how to flirt with a girl | http://www.howtoturnagirlon.com - how to turn a girl on
Posted by: Account Deleted | May 10, 2011 at 06:27 AM
Cs4 is much more application now bec. of air. I'm still stuck with cs3.
http://www.ripplewerkz.com
http://www.ripplewerkz.com/singapore-web-design.php
Posted by: web_design | April 20, 2011 at 08:46 PM