01. Introduction
With the arrival of Flash MX 2004, Macromedia introduced a new version of the scripting language for Flash - ActionScript 2.0. In this tutorial, we'll go through new language features, the new object-oriented programming model, and see examples of migrating ActionScript 1.0 (from Flash 5 and MX) to ActionScript 2.0.
Before we start on the tutorial, make sure you download the source files and unzip them into your working directory. For abbreviation, you'll see references to ActionScript 1.0 & ActionScript 2.0 as AS1 & AS2.
Why do we need a new version of ActionScript?
Well, if all you're making are simple animations or ads you don't really need a new version of ActionScript,. In fact, many of these projects don't even require ActionScript! If I stop here, this will be a rather short tutorial on what you don't need ActionScript for.
So here we go...
As developers use Flash for building more complex applications, ActionScript 1.0 quickly feels under-powered or over-laboured. For mainstream developers who are used to languages such as Java, C++ or C#, ActionScript 1.0 looks and feels foreign. It requires some mind-bending to overcome the initial learning curve, especially if they're trying to develop object-oriented applications.
In ActionScript 2.0, these programmers will find the new syntax familiar. AS2 feels like Java. JScript.NET and javascript 2.0 developers will feel right at home because all three languages came from the same background (technically they're all based on the ECMAScript Edition 4 proposal). ActionScript 2.0 lets developer build more robust applications, and perhaps will also attract more enterprise developers to Flash.
Okay, maybe you're not into building applications... Are you developing (or would like to develop) games? How about interactive presentations? The good news is, ActionScript 2.0 offers benefits for these types of development as well.
Don't worry if you're not interested in learning ActionScript 2.0 right away. Flash MX 2004 lets you continue using ActionScript 1.0 if you wish, you just won't be able to take advantage of some of the new features. In fact, under the hood, the compiler turns ActionScript 2.0 code into ActionScript 1.0 equivalent bytecodes. The reason for this unexpected feature is to allow developers to code in AS2 and still compile to Flash Player 6 for backwards compatibility. Macromedia calls ActionScript 2.0 a "syntactic formalization of the prototype chaining method". So, yes, the familiar prototype chain is still there, behind the scenes.
02. A little bit of OOP in ActionScript 1.0
Before we move on to ActionScript 2.0, let's review OOP in ActionScript 1.0. This section is especially useful for readers who are not familiar with OOP in Flash 5 and Flash MX. Experienced developers may skip ahead to What's new in ActionScript 2.0?
Although ActionScript 1.0 is not a true OOP language, developers have been programming object-oriented applications with it for some time. Everything in AS1 depends on the prototype chain -- the underlying structure of how objects are related. As a result, OOP in ActionScript 1.0 requires a knowledge of this prototype chain (or more specifically, the prototype keyword).
ActionScript 1.0 classes are implemented just like regular functions. Methods (and sometimes properties) are attached to the prototype object of the class. For example:
// Wizard class
function Wizard() {
}
// A method called help() is attached to the prototype // object of the Wizard function
Wizard.prototype.help = function() {
};
When Flash looks for a property or method of an object and can't find it inside the object, it searches along the prototype chain of the object. If this help() method is placed inside the class instead, Flash creates a new copy of the method for every instance of the Wizard class:
// This creates a new copy of the function help() in every instance
function Wizard() {
this.help = function() {
};
}
To Java, C++ or C# programmers, this syntax looks more familiar, with the method inside the class. However, in order to save memory (by sharing the same code at one location), methods (and shared properties) should be placed in the prototype object of the class.
In the following example, if we have two methods of the same name, one attached to the prototype object and one inside the class, Flash will reach the internal method first (it overrides the prototype method):
// AS1_OOP_01.fla
function TestClass() {
this.method = function() {
trace("Internal method");
}; this.prop = ">>> Internal prop";
}
// Attach a method to the prototype object of the class
TestClass.prototype.method = function() {
trace("Prototype method");
};
TestClass.prototype.prop = ">>> Prototype prop";
// Create an instance of the TestClass class
var w = new TestClass();
// Internal method is located before the prototype method
w.method();
// Replace the Internal method
w.method = function() {
trace("New method");
};
w.method();
// Delete the Internal method
delete w.method;
// The only method remaining is the prototype method
w.method();
// Test the properties
trace(w.prop);
w.prop = ">>> New prop";
trace(w.prop);
delete w.prop;
trace(w.prop);
The output of this example is shown below.
As you can see, OOP in ActionScript 1.0 can be rather confusing for beginners. Knowing where to put code is important because undesirable results may occur that can lead to hair lost debugging the code. As they say in commercials: But wait, there's more...
One of the important concepts in OOP is Inheritance. Basically you get whatever your parents and ancestors have, for free. If you don't like whatever they pass on to you, you can change them to your liking. Inheritance in ActionScript 1.0 is officially coded like this:
function SuperClass() {
// SuperClass constructor
}
SuperClass.prototype.parentMethod = function() {
};
// A subclass that inherits from SuperClass
function SubClass() {
super();
}
// Set up the inheritance relationship: SubClass inherits from SuperClassSubClass.prototype = new SuperClass(); SubClass.prototype.childMethod = function() {
};
There are a couple of issues with this set up:
The syntax is non-standard: How does over-writing the prototype object with an instance of the superclass establish inheritance? No other OOP language works like this. In effect, this is what SuperClass is saying to the SubClass: "If you want to inherit from me, first create a new instance of me, and inherit from it, don't inherit from me directly." What happens is, there are all these nameless instances of superclasses hanging around, taking up memory, just to define the parent/child relationships.
Anything placed in a class's prototype object is wiped out, so make sure you don't have anything attached to it before inheriting another class.
While setting up the relationship between the SubClass and SuperClass, the new statement invokes the SuperClass constructor. This limits what can be executed inside the constructor of superclasses. For examples, if you are incrementing a global value in the constructor, or creating and displaying movieclips, you'll end up with extra values or unwanted movieclips.
There are workaround solutions, for example, using __proto__ to set up the relationships directly, but it gets even more confusing for programmers who are new to ActionScript. The fact is, OOP in ActionScript 1.0 takes more effort than it should, because the language wasn't really designed for true object-oriented programming.
The benefits of OOP are numerous (such as more maintainable, scalable and reusable code). If we can achieve the benefits without all that mess, it is worth every bit (and byte) of the effort! The ECMA-262 Edition 4 proposal addresses most of these issues, and ActionScript 2.0 takes advantage of this proposal by introducing a new OOP model and concepts.
03. What's new in ActionScript 2.0?
ActionScript 2.0 is not a new language, it is an enhancement to the previous version. Learning it is actually quite easy if you have already been programming in ActionScript 1.0 (or if you have other OO language background). Let take a look at what ActionScript 2.0 offers:
Strict data typing and improved compiler warnings
Code hinting based on data type
New keywords and features for OOP:
class
interface
extends
implements
public
private
static
dynamic
intrinsic (intended for Macromedia internal use only)
import
class path (package)
get
set
Let's take a look at each one of these new features.
Strict data typing
In strongly-typed languages, expressions are associated with data types. The three major benefits of strict typing are:
It helps the compiler to detect potential problems and catch data type mismatch errors.
It makes code easier to read and understand with clearly specified data types .
It allows early-binding of data into appropriate object types for runtime optimization.
Because Flash MX 2004 only supports compile-time strict type checking, defined data types are not exported to SWF files and there is no runtime type checking or early-binding optimization. Also, ActionScript 2.0 does not support machine native data types such as those found in other languages (including JScript.NET): int, long, float...etc.
In ActionScript 1.0, declaring a variable 'count' looks like this:
var count;
In ActionScript 2.0, with strict typing, this statement can be written as:
var count:Number;
Note: The syntax
: may seem strange to some developers (languages such as Java, C/C++ and C# declare the type before the variable). This was a decision that the ECMA-262 committee made, and Macromedia is simply following the specifications.
ActionScript 2.0 supports strict typing for variables, function arguments and return values. For example, the following function accepts one argument of type String, and returns a Boolean value.
function func(arg:String):Boolean {};
When a function does not accept any argument or return any value, use Void as the data type:
function func(Void):Void {};
The way the argument is written (as Void) may seem strange, but it is used throughout component code. It simply signifies that there are no expected argument. However, if you try to pass arguments to this function, the compiler does not generate any error warnings, and the function will receive the arguments.
Although strict typing is available in ActionScript 2.0, Flash does not enforce the strict typing syntax. However, if the compiler checks and finds a mismatch with one of the classes that is being used, a warning will be displayed. It is a good habit to start using strict typing because of the benefits mentioned above. If you do use strict typing, you must specify "ActionScript 2.0" in the movie publish settings. However, you don't have to publish to Flash Player 7 as AS2 can be compiled for Flash Player 6 (even better, choose to export for player version 6.0.65.0 (6 r65) or higher for better optimization).
With strict typing, the new compiler now offers many new warnings and prevents movies with mismatched data types from compiling. This can be a big time saver when it comes to debugging code.
Code Hinting
In Flash MX, code hinting is usually based on variable name extensions such as _pb for Push Button, or _cb for ComboBox (another method is to declare the code hinting pattern in comment-styles notations such as // MovieClip myClip. In Flash MX 2004, you'll find that most of these extensions no longer work by default. The new version 2 components use a different approach to code hinting.
When a variable type is specified (and if the code hint information for the class is available), methods and properties of the class are displayed at the end of the dot; otherwise, no code hinting is displayed. By specifying the data type, typing the following brings up the code hint dialog box for ComboBox:
var combo:mx.controls.ComboBox;
combo.
You can check out the default name extensions by opening the following file in a text or XML editor:
\\First Run\ActionsPanel\AsCodeHints.xml
For English installations, is "en".
Tip: Adding Flash MX's code hints to Flash MX 2004
If you are using Flash MX 2004 to edit Flash MX movies with version 1 components, or when you need to publish for playback in all versions of the Flash Player 6, you can make some changes to use Flash MX's default code hints:
Open and copy the section from the end of Flash MX's UIComponents.xml (located at \\First Run\ActionsPanel\CustomActions\UIComponents.xml), as shown below.
Paste it just before the ending tag of Flash MX 2004's UIComponents.xml.
Paste the same block in Flash MX 2004's AsCodeHints.xml (but remove the tags)
This lets you use Flash MX version 1 component code hints in Flash MX 2004.
Another method to bind variable names to (most) built-in data types is to use the comment-style notation (that Flash MX also supports):
// DataType variable;
For example, if you want to declare the variable myClip as a MovieClip, you can place this code before you use the variable:
// MovieClip myClip;
From now on, typing myClip. brings up code hinting for MovieClip. However, this does not work with non-built-in classes such as mx.controls.ComboBox; for these, use the var notation as discussed above instead.
04. Checkbox Components
The Checkbox component is usually used on forms where a question can have more than one answer (for example, a "Click all that apply" type question.) Typically, there are two ways that a checkbox can function within your application: active and passive. For the purposes of this tutorial, I'm going to classify an active checkbox as one that affects something within the application the moment it is checked or unchecked. For example, unchecking a certain checkbox might disable a certain portion of the form with additional questions linked to the item being unchecked. A passive checkbox, on the other hand, is one that we can safely ignore until a form is submitted. In the first exercise, you will create a simple active checkbox which, when clicked, will echo the state of the checkbox into a TextInput component.
Start a new FLA and drag a Label Component and a CheckBox component on the Stage, as shown below.
Using the Property Inspector (PI), give the label component the instance name myLabel.
Similarly, give the Checkbox component an instance name of myCheckbox and set its label to " This is an important option". (Notice the space before the first letter of the label for formatting purposes.)
Notice that the Checkbox component's default size cuts off the label you just entered. To fix this, select Modify -> Transform -> Free Transform (or the Free Transform tool; Q) and resize the Checkbox component to make it longer.
Similarly, use the Free Transform tool to resize the Label component to make it wider so that it does not cut off the message you are eventually going to display in it.
Create a new layer and call it actions and enter the following script on Frame 1 of the Actions layer:
// create event listener object for checkbox
myCheckboxListener = new Object();
// click event handler
myCheckboxListener.click = function ()
{
if ( myCheckbox.selected )
{
myLabel.text = "You have checked the checkbox!";
}
else
{
myLabel.text = "You have unchecked the checkbox!";
}
}
// register the event listener
myCheckbox.addEventListener("click", myCheckboxListener);
// clear the label
myLabel.text = "";
Test the movie to see what it does. Check and uncheck the Checkbox and note the resulting text in the Label component.
Look through the code to understand how this is achieved. You should, by now, be familiar with how to add a listener to any component (if you are not, read through the Button and Alert component examples again.) Here, you have set up a listener to listen for the click event on the Checkbox component. When the user clicks on the Checkbox component, the click event handler gets called and it is here that you check the selected property of the Checkbox component to see whether it is checked or unchecked. Accordingly, you alter the text property of the Label component to display the current state of the Checkbox.
Although you may use active checkboxes from time to time in your applications, you will, for the most part be passively evaluating the values of checkboxes when a form is submitted. The next example shows you one possible way to achieve this.
Start a new FLA and use the screenshot below as a guide when laying out your movie.
Create two new layers and name your layers, starting with the top one, as Actions, Form Elements and Background Fills, respectively.
Drag a TextArea Component on stage and place it towards the very top of the Stage. Using the Property Inspector (PI), give it an instance name of statusMessage and set its text property to Please fill out the form below and hit submit. Make sure that wordWrap is set to true.
(Optional; Eye-Candy) Underneith the TextArea Component, use the Text Tool (T) to create a Static Text field and enter the following text in it: I am... (check all that apply).
(Optional; Eye-Candy) In the Background Fills layer, create a light blue rectangle.
Drag four Checkbox components and place them one under the other as shown in the screenshot in Step 1.
Using the PI, give the Checkboxes the labels you see in the screenshot in Step 1. Give the Checkbox components the following instance names, starting with the one at the top: checkboxAmazing, checkboxLovable, checkboxIntelligent and checkboxOutToLunch. The PI settings for the topmost checkbox are shown below.
Drag a Button component on stage and place it at the bottom of your form. Give the button component an instance name of submitButton and change its label to read "Submit".
In this example, you are not concerned with doing anything when the user clicks on a specific checkbox. However, you will want to do something based on which checkboxes are checked once the user submits the form. In this case, let's say that you want to change the text in the TextArea component to display a message reflecting the adjectives the user has chosen to describe herself.
Enter the code in the following steps in Frame 1 of the Actions layer, in the order stated.
To start, set up a reference to refer to the timeline that the form is on. In this case, this will simply refer to _root. However, if this movie is at some point loaded in by another movie (as a child movie), myForm will still contain the correct reference to the timeline. An absolute reference to _root (e.g. myForm = _root) would break in such a scenario, unless _lockroot is set to true for the movie. In my experience, it pays to never use absolute references to _root in an application.
// save a reference to the timeline that contains the form
myForm = this;
Next, you need to somehow tie together the four checkboxes since they all refer to the same question. For the purposes of this example, you can do this simply by adding a new property to each of the textboxes called group and setting the value of this property to the same string constant, as shown below. This will let you find and check the states of the checkboxes easily later on without having to hardcode the checks using the instance names of the checkboxes (this is more scalable since it means that you won't have to modify the code if you decide to add or remove a checkbox later on.)
// create a common group for our
// checkboxes by setting a property called group
GROUP_NAME_STR = "firstQuestion";
checkboxAmazing.group = GROUP_NAME_STR;
checkboxLovable.group = GROUP_NAME_STR;
checkboxIntelligent.group = GROUP_NAME_STR;
checkboxOutToLunch.group = GROUP_NAME_STR;
Since you want to carry out the processing of the form when the form is submitted, you need to set up a listener to listen for the click event on the Submit button and register it as a listener. Write out the skeleton code for this as shown below.
// create a listener object for the submit button
submitButtonListener = new Object();
// submit button listener, click handler method
submitButtonListener.click = function ()
{
// todo
}
// make the submit button listener listen for click events
submitButton.addEventListener("click",submitButtonListener);
In the step above, you left the body of the actual click handler empty. This is where the bulk of your form processing will take place. In order to keep the code more legible and uncluttered, you may even want to call a different method from the click handler to handle the form processing. For the purposes of this tutorial, you can place the form handling logic inside the handler, as shown below.
Modify the click handler method on the submitButtonListener so that it matches the listing shown here.
// submit button listener, click handler method
submitButtonListener.click = function ()
{
// user clicked the submit button
// start the message we are going to display:
INITIAL_MESSAGE_STR = "You are";
var msg = INITIAL_MESSAGE_STR;
// the string we use to join adjectives
AND_STR = " and";
// search for the checkboxes on the form
for (var i in myForm)
{
var currentFormItem = myForm [ i ];
// is the current form item a checkbox that is
// part of our group that contains adjectives?
if (currentFormItem.group == GROUP_NAME_STR )
{
// is the checkbox checked?
var isChecked = currentFormItem.selected;
if ( isChecked )
{
// add the current adjective to the message
msg += currentFormItem.label + AND_STR;
}
}
}
// check if the message is still equal to the initial message
// (ie., the user has not checked any of the boxes)
if ( msg == INITIAL_MESSAGE_STR )
{
msg += " not one to click checkboxes, it appears!";
}
else
{
// remove the last "and", it's extra
var numLettersToRemove = AND_STR.length;
var msgLength = msg.length;
msg = msg.substring ( 0, msgLength - numLettersToRemove );
// don't forget the trusty full-stop.
msg += ".";
}
// change the text in the TextArea component at the top
// to the message we just created
statusMessage.text = msg;
}
Test the movie. Click on some of the checkboxes to select them and then click on the Submit button and note the message that appears in the TextArea component.
The majority of the work in this example is performed in the click handler of the submitButtonListener. Here, you go through all of the various elements on the current timeline (myForm), and check to see if they have a group property that is set to the string constant we defined in GROUP_NAME_STR. When you find one that fulfills this test, you can be sure it is one of the four checkboxes that belongs to your question and thus continue to check its selected property to see whether or not is it checked. If it is checked, you add its label to the string you are composing. The rest of the code deals with making sure that the message makes grammatical sense in English.
05. Radio Buttons
Unlike checkboxes, where you are allowed to select more than one for a given question in a form, you can only select one radio button from a radio button group. With radio buttons, the concept of a group is central to their use. They are used when a question requires a single answer (a "Select one of the following" type question.)
As with checkboxes, it is definitely possible to give radio buttons an active behavior by listening for the click event and I won't go into that here since it was discussed in detail for checkboxes in the previous section and the technique is the same. Instead, the example below will show you how to set up a form with radio buttons and passively inspect them when the user submits the form. The structure of the example is closely related to that of the Checkbox component example, above.
Layout the Stage using the screenshot below as a guide. The screen layout is essentially the same as the one you used for the Checkbox component example, above, so you can refer to the detailed instructions there if you feel lost at any point. The only difference is that you are replacing the four Checkbox components with four Radio Button components.
Give the first radio button the label Amazing and the instance name radioButton1. The most important property here is groupName. This is how we tell Flash that this radio button is part of the same group as the other three. For the groupName, enter myAdjective.
Similarly, set up the second radio button, using the screenshot below as a guide. Make sure you give it the same groupName as the first radio button.
Set up the third radio button using the Property Inspector, as shown below.
Set up the fourth radio button, as shown below.
Now that the radio buttons have been set up, enter the following code in Frame 1 of the Actions layer:
// save a reference to the timeline that contains the form
myForm = this;
// create a listener object for the submit button
submitButtonListener = new Object();
// submit button listener, click handler method
submitButtonListener.click = function ()
{
// find the selected radio button in the group
var selectedRadioButton = myAdjective.selection;
// store the label of the selected radio button
var selectedRadioButtonLabel = selectedRadioButton.label;
// compose and display the message
statusMessage.text = "You are " + selectedRadioButtonLabel;
}
// make the submit button listener listen for click events
submitButton.addEventListener("click", submitButtonListener);
Test the movie, select one of the radio buttons and hit the Submit button and note the message that is displayed in the TextArea component.
If you have been following along from the Checkbox component, you will notice that the code in the submitButtonListener's click handler is much simpler for the Radio Button example. For one thing, you don't have to search in the form to find the correct components and then query them for their status. Instead, you can simply ask the radio button group to return a reference to the selected radio button. You created the radio button group by setting the groupName properties of the radio buttons in the Property Inspector in steps 2 to 5. Once you have the reference to the selected Radio Button component, you can easily get its label and compose and display a message within the TextArea component stating the user's selection.
06. The List Box family of Components
There are three components that fall into the List Box family of components and are very similar in the way that they work. These are the List Box, Combo Box and the Grid component. Instead of creating separate examples for each of them, let's examine them together in a running example that includes all three. In doing so, you will see the similarities between them and also the different ways in which to populate and use them. Use the following screenshot as a guide when laying out your example Flash application.
Start a new FLA.
Create a new layer. Label the topmost layer Actions and the one below it, Form Elements.
Drag a ComboBox, ListBox and Grid component on Stage and place them as shown in the screenshot, above. If you wish, you can label each with a Static Textfield, as I have done, to make it easier for you to differentiate them (although their Live Previews should be enough for you to do this just by looking at them.)
Using the Property Inspector, give the ComboBox the instance name myComboBox. Similarly, give the ListBox and Grid components the instance names myListBox and myGrid.
Combo Box
The ComboBox component is one of the easiest components to use. It allows you to display a list of data but does not take up as much space on screen as a ListBox component. Unlike a ListBox component, which can default to not having any of its items selected, the first item in a ComboBox is selected by default. The first item in a combo box is usually a prompt for the user to select one of the other items (e.g. "Please select your country...")
Without further ado, let's jump in and see the simplest way to use a ComboBox. In this example, you will be setting the labels of the ComboBox manually using the Property Inspector (PI). In the example with the ListBox, later, you will see how to do this through code.
Make sure you have followed instructions 1-4 in the section on The List Box family of Components, above, to set up your FLA.
Click on the myComboBox component instance to select it and enter four labels for it by clicking on the Labels cell and using the values dialog. (Use the + button in the values dialog to create the new label values.) It is possible, though not necessary, to give each item in a ComboBox a separate data value. In the next example, you will see an application where this will be necessary, but you can ignore it for now.
Enter the following script in Frame 1 of the Actions layer:
myComboBoxListener = new Object();
myComboBoxListener.change = function(eventObj)
{
var eventSource = eventObj.target;
var theSelectedItem = eventSource.selectedItem;
var theSelectedItemLabel = theSelectedItem.label;
trace ( "You selected "+theSelectedItemLabel+".");
}
myComboBox.addEventListener ("change", myComboBoxListener);
Test the movie, select an item from the ComboBox and note the message that gets traced out into the Output window.
In this simple example, you set up a listener for the ComboBox to listen for change events. Change events get fired whenever the user changes the selected item in the Combo Box. The event handler gets called with an Event Object that has a target property that points to the ComboBox instance that the event originated from. You can get the selectedItem object for this ComboBox instance using its selectedItem property. The selectedItem object, in turn, has data and label properties. The handler ends by tracing out the label of the selected item to the Output window.
Although easy to use, the ComboBox is also quite powerful and contains a myriad of events and methods for doing anything from responding to the Enter key being pressed on a ComboBox instance to when the user rolls over (as opposed to selects) an item within the ComboBox.
You may be wondering why I grouped the ComboBox along with the List and Grid components since, in appearance at least, it is very different. The reason is because the ComboBox actually contains a List Box -- the drop down that appears is none other than an instance of the List Box component, which you will be seeing next.
List Box
In the Combo Box example, above, you saw how to populate a list of labels manually, using the Property Inspector. Although it is definitely possible to do things this way (and perhaps even desirable for quick and dirty applications), more often than not you will be receiving this data from the server side and will need to populate a Combo Box or List Box dynamically at runtime. In this example, I will show you how to do this using the List Box.
Make sure you have followed instructions 1-4 in the section on The List Box family of Components, above, to set up your FLA.
Add the following script to Frame 1 of the Actions layer:
// create the items
var item1 = {label: "Organic cotton underwear", data: 7.25};
var item2 = {label: "Organic T-Shirt", data: 15};
var item3 = {label: "Recycled Office Paper", data: 6.99};
var item4 = {label: "Organic Cola", data: 1.25};
// populate the list box
myListBox.addItem(item1);
myListBox.addItem(item2);
myListBox.addItem(item3);
myListBox.addItem ( item4 );
// listener
myListBoxListener = new Object();
myListBoxListener.change = function ( eventObj )
{
var eventSource = eventObj.target;
var theSelectedItem = eventSource.selectedItem;
var theSelectedItemLabel = theSelectedItem.label;
var theSelectedItemData = theSelectedItem.data;
trace(theSelectedItemLabel+" costs £" +theSelectedItemData);
}
myListBox.addEventListener ("change", myListBoxListener);
Test the movie and click on the various items in the List Box and notice the messages traced out in the Output window.
The code in this example is pretty much self-descriptive. First you create the four items as objects with label and data properties and then use the addItem() method of the List Box component to add them to the component. Finally, you create a listener for the change event, just as you did for the Combo Box example and trace out the cost of the currently selected item.
The Grid
The most complex of the the three List Box family of components is the Grid. You can think of a Grid as a multi-column list box. In fact, this is exactly how the component was architected. Unlike the List Box and Combo Box components, which can only have label and data properties in their data sources, the Grid can have any number of custom columns. In this example you will see how to set up a three column grid.
Make sure you have followed instructions 1-4 in the section on The List Box family of Components, above, to set up your FLA.
Add the following script to Frame 1 of the Actions layer:
// create the items
var item1 = {Product: "Underwear", Price: 7.25, Quantity: 3};
var item2 = {Product: "T-Shirt", Price: 15, Quantity: 1};
var item3 = {Product: "Paper", Price: 6.99, Quantity: 7};
var item4 = {Product: "Cola", Price: 1.25, Quantity: 24};
// populate the grid
myGrid.addItem(item1);
myGrid.addItem(item2);
myGrid.addItem(item3);
myGrid.addItem(item4);
// listener
myGridListener = new Object();
myGridListener.change = function ( eventObj )
{
var eventSource = eventObj.target;
var theSelectedItem = eventSource.selectedItem;
var theSelectedItemName = theSelectedItem.Product;
var theSelectedItemPrice = theSelectedItem.Price;
var theSelectedItemQuantity = theSelectedItem.Quantity;
trace
(
theSelectedItemName
+ " costs £" + theSelectedItemPrice
+ " (" + theSelectedItemQuantity + " left in stock)"
);
}
myGrid.addEventListener("change", myGridListener);
Test the movie and click on the various items in the Grid and notice the messages traced out in the Output window.
Notice, if you will, how similar the code in this example is to the code in the List Box component example. The difference is that the item objects for the Grid contain custom properties (Product, Price and Quantity) instead of label and data. Also notice how we can query the selected item in a Grid directly to learn the values of its properties. The trace in the change() event handler is broken up over several lines to make it easier to see the message being constructed. Finally, note that the Grid automatically creates your header fields for you using the names of the properties in your items. This is actually a mixed blessing: For the names to display correctly, you had to use capital letters to start the names of the properties. This is actually bad practice. Property names, like method names should start with a lowercase letter.
There is a way to display a column name in the header of the grid that is independent of the name of the property for that column, but it is messy. Here's one way of doing it:
Alter the script in Frame 1 of the Actions layer to match the listing below:
// create the items
var item1 = {product: "Underwear", price: 7.25, quantity: 3};
var item2 = {product: "T-Shirt", price: 15, quantity: 1};
var item3 = {product: "Paper", price: 6.99, quantity: 7};
var item4 = {product: "Cola", price: 1.25, quantity: 24};
// populate the list box
myGrid.addItem(item1);
myGrid.addItem(item2);
myGrid.addItem(item3);
myGrid.addItem(item4);
// set the displayed header independently of the
// column (property) name
myGrid.getColumnAt(0).headerText = "Product Name";
myGrid.getColumnAt(1).headerText = "Price";
myGrid.getColumnAt(2).headerText = "Quantity Left";
// listener
myGridListener = new Object();
myGridListener.change = function ( eventObj )
{
var eventSource = eventObj.target;
var theSelectedItem = eventSource.selectedItem;
var theSelectedItemName = theSelectedItem.product;
var theSelectedItemPrice = theSelectedItem.price;
var theSelectedItemQuantity = theSelectedItem.quantity;
trace
(
theSelectedItemName
+ " costs £" + theSelectedItemPrice
+ " (" + theSelectedItemQuantity + " left in stock)";
);
}
myGrid.addEventListener("change", myGridListener);
Test the movie and notice how the column names displayed in the header differ from the property names used for the columns.
The problem I have with this method is that it should not be this hard to give a column a name that is separate from the name of its data property. Almost any application that uses a grid will require this functionality.
If you haven't been following along in Flash, you can find the completed FLA with all three of the previous examples, combo_list_grid.fla, in your downloads. You can play with a slightly modified version, below, where I've replaced the traces with messages displayed in a TextArea component. (To see how to do this, see the exercises in the Check Box and Radio Button sections.)
有能力,有时间并且有兴趣的可以帮大家译一下:)