Working with Components - Foundation ActionScript 3, Second Edition (2014)

Foundation ActionScript 3, Second Edition (2014)

Chapter 9. Working with Components

This chapter covers the following topics:

· Components in the context of ActionScript

· The benefits of working with components

· The makeup of the Flash component framework

· Scripting interactions with components

· The process of skinning components

· How to build your own components

· How to get more components

Up to this point in the book, you’ve been working with the objects in ActionScript that are built into the core language. These objects include the display list items Sprite and MovieClip; and top-level objects such as Array, String, and Number. For additional functionality, you’ve explored building your own classes from scratch or extending the existing classes, as in the iPod example in Chapter 3.

Sometimes, other developers have built additional functionality that you can use within your own projects. This functionality might be in the form of a class Library from which you could create instances. Often, in the context of Flash and ActionScript, the additional functionality comes in the form of a special kind of class Library built off the display list, providing drag-and-drop widgets to place in your applications or instantiate through code. These drag-and-drop widgets are called components in Flash lingo. If you understand what components are and what they can do, they can help speed up your development and add some pretty cool features to your projects.

If you’re a Flex user, note that this chapter is necessarily focused on the Flash CC IDE and its components framework. The Flex framework is a more robust and complex application framework that includes its own UI component classes—many more than are available by default in Flash. However, the concept for scripting interaction between components is still applicable. And perhaps more important, components can be developed using the graphical timeline in Flash CC for use within Flex.

Just What are Components?

Components are items in your Library just like any other movie clip symbol. In fact, all components must extend Sprite or MovieClip, and so are, by default, display objects that can be added to the display list. On that level, they are not much different from a graphic object drawn and then turned into a movie clip symbol. At that point in the game, each symbol instance can have different properties set—such as x, y, alpha, and rotation—all done visually on the stage in the IDE.

What makes components different and special is that in addition to the familiar display object settings, any number of additional parameters might be available for you to set in the IDE (through the Parameters panel). For example, the CheckBox component that comes with Flash CC has three additional parameters not standard to other sprites, but important for check boxes: label, labelPlacement, and selected.

So, in that light, components are extended sprites or movie clips that add specific functionality, usually for use within a UI. Components available with the standard installation of Flash CC include Button, CheckBox, ColorPicker, ComboBox, and many more. You can place instances of each type of component on the stage and configure them to act differently from other instances, so that two Button components could have two different labels, and three ComboBox components could have different lists of items.

Although components are display objects and are generally UI widgets, that is not to say that they must be. A component might be nonrendering, meaning it would not draw any graphics within itself and would simply provide additional functionality for a movie. In that case, it would be only a Library item so that it could be dragged onto the stage and configured using the Parameters panel, but at runtime it would be invisible, or perhaps purposely remove itself from the display list. We won’t explore this nonrendering type of component in this chapter, but you should be aware that these are valid forms of components as well.

Accessing Your Components

To add a component to your Flash file, you can drag it in from the Components panel or simply double-click it in the Components panel, and it will be added to the center of the stage. You can open this panel by selecting Window image Components from the main menu. One folder in theComponents panel contains general UI controls such as Button, List, and Slider. The other folder contains controls for video playback, including the FLVPlayback component. With both of its folders expanded, this panel should appear as shown in Figure 9-1.

image

Figure 9-1. The Components panel with its two folders expanded

To add any of these components to your file, simply drag and drop the items onto your stage or directly into your Library. In either case, the components will then appear in your Library. You can drag additional instances from your Library, as opposed to having to access the Componentspanel again.

Some components are made of multiple subcomponents, so don’t be surprised if by adding one component to your file you get several in your Library. For instance, if you add the ComboBox component to your Library, you’ll find that the List and TextInput components come with it, as shown in Figure 9-2. This is one advantage of the modular style in which components are often created. With the ComboBox component added to your file, you can also create instances of the TextInput or List component without any additional file size cost. Nice!

image

Figure 9-2. A Flash file’s Library after the ComboBox component has been dragged into it, bringing its ­subcomponents along

Of course, you do not necessarily need to drag and drop component instances onto the stage to use them. Just as with any other symbol, once a component is in the Library, you can create a new instance by using the new keyword in ActionScript (hey, this is still an ActionScript book, right?), as long as it is set to be exported for ActionScript in the first frame, which all Flash CC components are by default. So, for instance, to create a new instance of the ComboBox class, you would use this code:

import fl.controls.ComboBox;

var combo:ComboBox = new ComboBox();
addChild(combo);

Keep in mind that when working with ActionScript and components, the component must also exist in the Library. Therefore the previous code will execute only if there is a ComboBox component in the Library.

Adjusting Component Parameters

Now you have components accessible in your Flash file through your Library, but how exactly can you configure an instance? Well, here’s the answer! When you have a component selected on the stage, you can use the Component Parameters panel from the Properties window (Windows image Properties) to populate components with all the wonderful options available for editing through the IDE for that component instance (there might be more available through ActionScript). Let’s take a look at an example.

1. Create a new Flash file for ActionScript 3.0. Open the Components panel if it is not currently open (Windows image Components).

2. Expand the User Interface folder in the Components panel and drag a Button component onto your stage. Notice that the button symbol now appears in your Library, along with the Component Assets folder, which contains the button symbol’s skins (among other things).

image

Figure 9-3. A Button component is added to the stage, and thus also appears in the Library

3. Select the Button instance on the stage if it is not currently selected, and then open the Properties window to find the Component Parameters panel.

Seven parameters are available under the Component Parameters group: emphasized, enabled, label, labelPlacement, selected, toggle, and visible (see Figure 9-4). The values in the left column of the grid are the names of the configurable properties. The values in the right column are the current values of the properties and are all editable.

image

Figure 9-4. The available parameters for a Button instance, accessed through the Component Parameters panel

4. Click the cell that says Label in the right column, which is the current value of the label property. The word becomes highlighted and you can overwrite it with your own string. Type in My Button and press Enter.

Did you notice what happened on the stage? Your Button instance now has My Button as its label, giving you instant feedback to your change, as shown in Figure 9-5. This sweet little feature is called Live Preview, and it allows components to update based on certain properties (unfortunately, some properties are not reflected; for instance, changing a skin does not update the skin of the Live Preview). Keep an eye on the instance on the stage as you edit to see how your changes are represented (or else the engineer who developed this fantastic feature will go cry in the corner).

image

Figure 9-5. The Button’s Live Preview shows the label updated with the new value

5. Click the checkbox in the second column next to the emphasized property. When checked, you will see the component update to emphasized.

6. Click the right value next to the labelPlacement property and select left from the list of four items. Nothing happens! In this case, the parameters here are a little misleading because they show options that are applicable only to coded items. labelPlacementaffects only Button instances that have icons (in which case the labelPlacement parameter controls on which side of the icon the label is placed), and icons can be set only through ActionScript. So changing this value here does nothing unless you write some code to add an icon to this or all Button instances.

7. Change the selected property from false to true by checking the checkbox. Again, nothing happens! In this case, it is because the selected property of a Button instance is directly tied to whether the button is set to toggle. A button that does not toggle cannot be selected. So if you change the toggle property to true as well, then you should see the Button instance change to show it is selected. If you test your movie, you should see that the instance starts out selected and can be toggled between states. Figure 9-6 shows theComponent Parameters and component after making all these changes to the properties.

image

Figure 9-6. The Button instance with its selected, toggle, and emphasized properties set to true

Don’t get frustrated by the Component Parameters panel. At this point, it pays to know where to find information about all your components and why properties might not be working as expected. That information is available through the ActionScript 3.0 Language and Components Reference in your Flash Help (select Help image Flash Help, press F1, or navigate to http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/index.html in your browser). From there, open the Classes folder, and you will see all ActionScript classes, including the components. You can browse each component’s properties here (as well as methods, styles, and events, with sample code) to investigate just what is configurable and how.

Well, there you go! The component framework in Flash CC and ActionScript 3.0 was intentionally simplified from the previous version to make components more lightweight. This makes the framework easier to work with for developing applications and movies using the Flash graphical environment (with the thought that more complex application development that requires more advanced features would be done using the Flex framework).

Benefits of Working with Components

We have looked at what components are, how to add them to your files, and how to configure them. A big remaining question is why you would want to do such a thing. Well, here’s a list:

· Rapid prototyping: Do you need to put an application together fast, fast, fast? Components, with their ready-to-use, often complex functionality, make it a cinch to put together an interface of standard UI widgets in no time.

· No need to reinvent the wheel: A button might not be a big deal to code in itself, but when you add on functionality such as icons, labels, and toggling, it gets a lot more complex than you might expect. Others have coded it already. Why not leverage the work already done?

· Easy to have minor differences in widgets: You need one button with a label; another with a label and an icon. You have one check box with the label on the right, another with the label on the left, and another with its label twice as big as all other instances. These minor differences between instances can be handled by a properly coded component, while keeping base functionality and graphical appearance consistent among all instances.

· Conforming functionality: One complaint against Flash interfaces is the lack of conformity among different applications, potentially causing frustration for the users who must “learn” an interface before using it. Using standard components, such as those available in the Flash component framework, helps to reduce this potential frustration.

· More OOP-like development: OOP is something that you are already doing with ActionScript, perhaps without really being aware of it. As explained in Chapter 1, OOP is a method of programming in which distinct, self-contained objects (think of Sprite and Arrayinstances) interact with each other in a recommended pattern that maintains a separation of responsibility. You don’t need to understand this fully or be a die-hard OOP proponent to know that by using self-contained widgets with clearly defined APIs, you are building in more of an OOP manner, which will make it easier to develop and maintain your code.

· Something for everyone: A lot of components are out there—not just the ones available with the standard Flash CC installation—that will do pretty much anything you could need. If you don’t have the time to develop from scratch, chances are someone else already has. The “Using third-party components” section at the end of the chapter discusses where you might find more components to use.

Exploring the Flash Component Framework

The ActionScript 3.0 components come in two broad categories: UI and Video components, and both sets can be accessed from your Components panel.

UI Components

The UI components consist of the following widgets:

· Button: This is the most common UI element, which allows a user to click for an action. The Button component also lets you set a label and/or icon, and allows for toggling behavior, meaning that the button can stay in one of two states: selected or unselected.

· CheckBox: Similar to the Button component set to toggle, this component has selected and unselected states, usually represented by a check mark within a box (the name says it all, doesn’t it?). A label, if added, is placed to the side of the box.

· ColorPicker: This component allows a user to set a color based on a pop-up swatch list of colors laid out in a grid. Alternatively, a color can be set by typing a hex value into a text field within the pop-up list. The close state of the ColorPicker shows a single swatch with the selected color.

· ComboBox: This is a drop-down list that displays a scrolling list of options, from which one can be selected. The selected option then appears displayed in the closed state of the component. A ComboBox instance can be set as editable, and the way a list item is rendered is completely configurable (meaning it does not need to be simply a text string; it can be an image, an image plus text, or anything you need to represent the data).

· DataGrid: Probably the most complex of the UI components, the DataGrid represents data in a scrolling grid of columns and rows, in which each row is a line item with possibly multiple values laid out across columns, each of which represents a single value. Each column can have a different way to represent its data, so that one column might have all its values as text, while another column might use images.

· Label: One of the simpler UI components, a Label is simply a text string that can be single- or multiple-line, and display regular or HTML text. The benefit of using Label components over TextField components is that the component framework makes it easy to change formatting across an entire application.

· List: The List component is useful for displaying arrays of values in a vertically laid out, scrollable region (the ComboBox, when open, displays a List instance). The way items are represented in the List component is completely configurable, just as in theComboBox, so you are not limited to merely a list of text values.

· NumericStepper: This component allows a user to set a single number from an ordered set of numbers, through a text field or by clicking up/down arrows that change the value of the text field. You can configure the range of numbers represented and the interval between numbers.

· ProgressBar: When an application is loading external data or media, it is best to offer the user feedback on the progress of the loading process, and that is where the ProgressBar component comes in handy. This component displays a graphical representation of the state of the load, either in a determinate manner (for example, showing a percentage of the bytes loaded in) or in an indeterminate manner (for example, showing a looping animation to assure the user that something is occurring, though it does not represent a percentage of bytes).

· RadioButton: This component is like a CheckBox in a group. When there are multiple RadioButton instances grouped together (programmatically, not graphically), only one option within the group can be selected at one time. This is great if there are a small number of options from which only one may be selected, and one must be selected for validation. A good example of this is male/female options on a user registration page.

· ScrollPane: This component offers a way to display other display objects and externally loaded images within a scrollable region. You can use this if you need to limit the area in which a display object may be viewed, but you want to allow the user to scroll to any area of that display object. This is useful not only for loaded images when there is limited screen real estate but also for forms that contain many controls, where you need to scroll vertically.

· Slider: This component allows the user to select a numeric value by dragging a slider thumb either horizontally or vertically within a defined region. The range represented, the interval between numbers that can be selected (for instance, if you only want integers or if you want decimal values), and the tick marks are configurable.

· TextArea: This component is a useful multiline input field that can optionally display scrollbars. If you need a user to enter any moderately large amount of data, or you wish to display such data (you can set a TextArea instance to be noneditable), this is a great component to use.

· TextInput: This component is the little brother of the TextArea component, allowing only a ­single line of input. This is useful for when smaller amounts of data need to be entered, like a username, a password, or an e-mail address.

· TileList: This component is most useful for displaying a scrollable grid of image data in a specified number of columns and rows (you can also configure the component to display only a single scrollable row or column). Like the other list components (List, ComboBox, and DataGrid), the TileList allows you to configure the way that tiles are displayed, so if the default method of displaying a tile isn’t what you need, you can create your own class to represent a tile in the manner required.

· UILoader: UILoader components are used to retrieve and display any content that exists outside of Flash. This content can include SFW, JPG, PNG, and GIF files. The UILoader component also gives you the ability to resize its contents.

· UIScrollBar: Scrollbars are ubiquitous and necessary in complex applications that are now commonplace, so thank goodness that the Flash component framework includes its own. Use the UIScrollBar component when you need to scroll a visual region. Often, using theScrollPane is easier for scrolling display objects that need to be masked, but the UIScrollBar is most useful for text fields that need to be scrolled when you don’t want to deal with a TextArea component.

Figure 9-7 shows most of the UI components laid out on the stage.

image

Figure 9-7. The UI components on display

Video Components

The video components are built a little differently from the UI components and are specifically targeted for playback and interaction with video. Only two of the video components are in the form of movie clip symbols with customizable parameters: FLVPlayback andFLVPlaybackCaptioning. The other components that appear in the Components panel under the Video folder—such as PlayButton, SeekBar, and VolumeBar—are skins that can be used to customize the controls of an FLVPlayback component. All you need to do is drag them to the stage, and they will automatically work with the FLVPlayback component; there is no parameter configurability.

Let’s take a look at the two configurable video components. The FLVPlayback component is shown in Figure 9-8, but the FLVPlaybackCaptioning component is nonrendering, so it does not appear in the screenshot of the published SWF.

image

Figure 9-8. An FLVPlayback component with its selected control skins

· FLVPlayback: This component fulfills almost all your video playback needs. It can load and play both progressively downloaded and streaming video, and there is a wide variety of customizable controls for such common functionality, such as play/pause toggling, volume control, back and forward buttons (when playing a list of videos), and a seek bar for showing the playhead position. Skins are all assigned from externally loaded SWFs and come with many different options.

· FLVPlayback 2.5: This is an updated version of the FLVPlayback component that adds support for dynamic streaming and DVR features of Adobe Flash Media Server 3.5.

· FLVPlaybackCaptioning: This component doesn’t actually have any graphic representation, but instead adds functionality to an FLVPlayback component. Using an FLVPlaybackCaptioning component, you can specify XML to load with your video. This XML can list time codes in a single FLV file or in multiple FLV files, text that should be shown at those times (represented in a format known as timed text), and the text formatting that should be applied. When the video plays back, the caption text will appear over it. Through code, you can have the text appear elsewhere by listening for change events and updating your own text display.

As mentioned previously, the remaining components in the Video folder in the Components panel are controls that can be used to build and skin your own layout for the FLVPlayback controls. The workflow for this would be to set the skins parameter of your FLVPlayback instance to None; then drag the desired controls from the Components panel to the stage in whatever configuration you need. The components are built so that the FLVPlayback component and the controls will automatically plug in to each other; you do not need any additional code to wire up things. You are then free to lay out and skin the controls however you need. See the “Styling and skinning” section later in this chapter for more on skinning components.

Going Behind the Scenes

Now that we’ve explored briefly what is available for use with the Flash component framework, let’s pop the hood and take a look at what’s going on to make everything run so smoothly.

1. Create a new Flash ActionScript 3.0 file to start fresh.

2. With the Components panel open, drag a Button component to the stage. At this point, you should see the Button symbol in your Library as well as the Component Assets folder.

3. Generally, you should never need to go into this folder and can access all you need through the component symbols (or their instances). But what’s the fun of looking under the hood if we honor that now? Open the Component Assets folder in your Library.

4. You will see three more folders: _private, ButtonSkins, and Shared. Expand ButtonSkins to see all the—you guessed it—button skin symbols, as shown in Figure 9-9. You can enter editing mode for any of these symbols to change their graphical appearance. However, a better option for this is to double-click the component symbol or one of its instances, as you will learn in the “Styling and skinning” section later in this chapter.

image

Figure 9-9. All the button symbol skins are accessible in the Library

5. Expand the Shared folder. You will see the focusRectSkin symbol. This symbol is used by many of the components to represent when they have keyboard focus. Several symbols will appear in this Shared folder, based on which components you have in your Library. The general rule is that any skin used by multiple components will appear here.

6. Expand the _private folder. You will see two symbols: Component_avatar and ComponentShim, as shown in Figure 9-12.

image

Figure 9-10. The contents of the _private folder in the expanded Component Assets folder

What are these goodies? The Component_avatar symbol is simply a rectangle that is used to size components in the IDE. Because components behind the scenes are just code, this graphic is used so that you can select and resize instances on the stage. At runtime, this graphic is removed from the display list in the component. (For all you users of the V2 components, this serves the same functionality as BoundingBox_mc.)

The ComponentShim symbol is much more interesting. To speed up the compile time of a movie, the component classes have been precompiled into SWC files. However, SWC files are not editable in the IDE. So, to keep components precompiled for speed and encapsulation, and yet still offer access for editing skins, all component classes have been precompiled into the ComponentShim symbol. This means that the component symbol, such as Button, is not precompiled, so it can be edited, but all the code used to define the Button functionality is precompiled into the ComponentShim symbol, along with all other UI components. This keeps the code tucked away and ensures faster compile times, yet still offers access to a component’s skins by double-clicking the symbol or an instance.

That’s pretty sweet! But if all the component classes are compiled into the ComponentShim, why doesn’t the file size increase enormously with the addition of a single component? After all, dragging a Button component into your file must also mean that you are getting DataGrid,TileList, and ScrollPane components, right? Actually, no. Much like when you include multiple import statements in your ActionScript referencing classes you do not use, the compiler knows to not include the unused component classes. Only if you include the additional components in your Library will you get the extra components compiled into your SWF because each component’s symbol in the Library is set to export for ActionScript.

Finding the Files

If you use components a lot, want to know better how they are put together, or need to make modifications, you should know where to find them in your file system.

First, you need to find the directory where you installed Flash. The default directory on Windows is C:\Program Files\Adobe\Adobe Flash CC, On a Mac, it is /Applications/Adobe Flash CC 2014, right-click the application and select "Show Package Contents". Within this directory, you should find a Common subdirectory where you will find Configuration and First Run directories. First Run will include files copied to your personal user directory the first time you launch Flash under a specific login. The Configuration directory is the one of interest now. Go ahead and expand that, and you should see subdirectories, as shown in Figure 9-11.

image

Figure 9-11. The Configuration directory in a default Flash CC installation

The two subdirectories of interest in this discussion of components are Components and Component Source.

Components Directory

If you select the Components directory, you will see two FLAs, as shown in Figure 9-12. Whatever is in this Components directory will appear in your Components panel in the Flash IDE. The two FLAs, which are named User Interface and Video, correspond to the two ­folders in the Components panel you’ve already explored.

image

Figure 9-12. The Components subdirectory in a default Flash CC installation

If you want to open the User Interface FLA, you have to copy it into another directory because Flash already recognizes this FLA as being open in the Components panel and will not open a second instance. Let’s do that now.

Select the User Interface.fla file in your file system and copy it to your clipboard. Select another directory, perhaps one associated with this chapter’s files in your personal folders, and paste the file there. Next, double-click the FLA to open it in Flash. You will see the stage appear, as shown in Figure 9-13.

image

Figure 9-13. The User Interface.fla open in the Flash IDE

This FLA is merely a container for the components so that they appear in a single folder in the Components panel. If you ever needed to modify the default skins that appear for any components, as opposed to editing in each file in which you use a component, you might edit the skins in the User Interface.fla file. In addition, if you ever wanted to add a component to the User Interface folder in your Components panel, you could add it to this file. In those cases, however, you would save the file under a new name and copy it into the Components directory within your user directory for your Flash configuration. For instance, on a Windows machine, the Components directory is located at C:\Documents and Settings\<user>\Local Settings\Application Data\Adobe\Flash CC\<language>\Configuration. If you did that and restarted Flash, a new folder would appear in your Components panel and would contain all the modified components, but the original UI components would remain untouched.

Component Source Directory

The second directory of interest in the Configuration directory is Component Source. Expand Component Source, and you will see an ActionScript 3.0 directory. Expand the ActionScript 3.0 directory to find FLVPlayback, FLVPlaybackCaptioning, andUser Interface subdirectories, as shown in Figure 9-14.

image

Figure 9-14. The Component Source directory contains all the ActionScript source for the component classes

Select the User Interface directory and notice that it contains a ComponentShim.fla file. This is the file that includes all the uncompiled component symbols for each class (not to be confused with the symbols that contain the skins).

Double-click the ComponentShim.fla file to open it in Flash. Notice that there is nothing on the stage, and no ActionScript in the timeline or document class is specified. However, the Library contains a symbol for each of the UI component classes, and each of them is set to export for ActionScript in the first frame. It is this file that is used to create the compiled symbol, which is then included within each component. If you want to modify the component source or add more classes to the ComponentShim symbol that appears in each component, this is the file you modify and recompile.

Now, the ComponentShim is a great trick for helping the compile times in complex applications with many components (and believe me, compile time can be a real pain in these circumstances), but what if you need access to that original source code that has been precompiled? Well, thankfully, Adobe has included the source for this purpose. Expand the fl subdirectory in the User Interface directory, and you will see all the packages containing the ActionScript files that are used in the components, as shown in Figure 9-15.

image

Figure 9-15. The ActionScript source code for all UI components is found in the User Interface directory in Configuration/Component Source/ActionScript 3.0.

Let’s try modifying the source code to see how it might work.

1. Expand the fl directory and select the core directory within this. You should see two ActionScript files: InvalidationType.as and UIComponent.as, as shown in Figure 9-16. All UI components inherit from UIComponent, either directly or indirectly, so if you add a trace() statement within this class’s constructor, you should see it appear in the Flash IDE’s Output panel.

image

Figure 9-16. The ActionScript classes found in the fl/core directory

2. Copy and rename UIComponent.as so that you have a backup, which is always a good idea when you are editing an original source file. Even though you will be adding only a single trace() statement, you want to be sure you can revert the file to its original condition, if necessary.

3. Double-click UIComponent.as so that it opens in Flash. Although it’s 1,500 lines of ActionScript, a quick look will show that the majority of the file contains comments, so it’s not as complex as you might first fear. Find the constructor at line 459, and add the single line of code shown in bold:

public function UIComponent() {
super();
trace(this);
instanceStyles = {};
sharedStyles = {};
invalidHash = {};

This will simply call the toString() method on the instance of the component that is being created and send the value to the Output panel.

4. Create a new Flash ActionScript 3.0 file and drag a Button component from Components panel on to the stage. Test your movie.

Nothing traces! Let’s think this one through. You know that the components have a ComponentShim symbol that contains all the precompiled component classes. Because the classes are precompiled, your trace() statement is not present (it wasn’t there when the classes were first compiled). So in order to modify this code, does that mean you need to recompile the ComponentShim and replace it in your file?

Thankfully, the answer to that is no. There is a nice trick to precompiled code, and that is if the Flash compiler finds uncompiled ActionScript in the classpath while compiling your SWF, it will use these classes as opposed to the precompiled versions. All that means is that if you point to the component source code directory, when you test the movie, the Flash compiler will grab the uncompiled source and ignore the precompiled code, and thus the trace() statement will be run.

5. Select File image Publish Settings. On the Flash tab, click the Settings button next to the ActionScript version drop-down list. This opens the ActionScript 3.0 Settings dialog box, as shown in Figure 9-17.

image

Figure 9-17. The ActionScript 3.0 Settings dialog box is where classpaths can be specified

6. With the Source Path tab selected, click the Browse To Path button that looks like a folder. This opens a browse dialog box. Navigate your file system to select the Configuration/Component Source/ActionScript 3.0/ User Interfacedirectory where the ActionScript source resides. Click OK, and the path will be entered into the Classpath list, as shown in Figure 9-18.

image

Figure 9-18. An example of multiple components working together to create a simple interface

7. Click OK to exit the ActionScript 3.0 Settings dialog box, and then click OK again to exit the Publish Settings dialog box. Test your movie again.

You should have noticed two things. First, the compiling of your movie should have taken slightly longer (not much, because there is just one component, but it might be noticeable). Second, the Output panel should have opened and traced [object Button]. So now you know that if you ever need to modify the component source, you can merely point to the source directory, and the compiler will grab the classes from there. Of course, as before, it is always recommended that you duplicate the original files before making any changes, so that you have a backup in case you need to restore the previous versions.

Scripting Interaction

Now that you have some familiarity with the components and an idea of what is going on behind the scenes, it’s time to plug some components together in a simple example. This will demonstrate how easy it is to work with the components and how they can be made to interact through ActionScript.

You will create a List instance that allows a user to add items to it through a TextInput instance and a Button instance. If an item is selected in the List instance, the user will be allowed to edit or delete the selected item. Such a control might be useful for any type of list that a user should be able to add to, such as a shopping cart, a buddy list, or a group of events. The end result will look like Figure 9-18.

Adding the Components

You’ll begin by adding the components to create the interface.

1. Create a new Flash ActionScript 3.0 file and save it as editableList.fla into a project directory for this chapter.

2. Select Modify image Document from the main menu to open the Document Properties dialog box. Set the width of the file to 230 px and the height to 200 px. Change the Background color option to a light gray (#CCCCCC), as shown in Figure 9-19.

image

Figure 9-19. The document properties for the editableList.fla file

3. Open the Components panel if it’s not currently open (Window image Components). Drag the Button, Label, List, and TextInput components on to your stage from the User Interface folder.

4. Select the List instance on the stage. In the Property inspector, change its dimensions to 200 × 100 and set its position to (15, 15). Leave all its default parameter settings, but give the instance the name names_li. The _li suffix makes it easy to see that the object is a List instance.

5. Select the TextInput instance. In the Property inspector, change its dimensions to 170 × 22 and set its position to (15, 120). Give it the instance name editName_ti (_ti for TextInput). Keep all its default parameter settings.

6. Select the Button instance. In the Property inspector, change its dimensions to 22 × 22 and set its position to (193, 120). Give it the instance name deleteName_bn (_bn for Button). In the Component Inspector, set the label property to (a minus sign).

7. Select the Label instance. In the Property inspector, change its dimensions to 200 × 20 with its position set to (15, 150). This instance does not need a name (you will not be manipulating it through ActionScript). In the Component Inspector, change its textproperty to Enter a name to add to the list. Figure 9-20 shows the results so far.

image

Figure 9-20. The first four components laid out on the stage

8. Drag another TextInput instance from your Library (Window image Library) to the stage. Give it the dimensions 170 × 22 and the position (15, 170). Give it the instance name addName_ti. All its parameters should remain at their defaults.

9. Drag another Button instance from the Library to the stage. Name it addName_bn. Make its dimensions the same as the other Button instance, 22 × 22, and set its position to (193, 170). In the Component Inspector, change its label property to + (a plus sign).

You are now finished with the interface itself, as shown in Figure 9-21. Using components makes it easy to lay out and align groups of components in the visual editor. Of course, to get those components to do anything useful, you’ll need some ActionScript to wire everything together.

image

Figure 9-21. The completed component interface for the editable list

Adding the ActionScript

Create a new ActionScript file and save it as EditableList.as into a package directory named com/foundationAS3/ch9 that is a subdirectory of where you saved the editableList.fla file from the previous steps. This will be the package structure for your ActionScript class.

In the ActionScript file, create the package structure, class declaration, and constructor for your EditableList class. Let’s also include the imports you will need, including the controls classes that are included in your interface. Note that these controls are found in the fl package, not the flash package that you have been previously using. The flash package contains all the classes built into the Flash Player runtime. The fl package contains classes provided for the Flash component framework that come with the Flash CC IDE installation.

package com.foundationAS3.ch9 {

import fl.controls.Button;
import fl.controls.List;
import fl.controls.TextInput;

import flash.display.Sprite;

public class EditableList extends Sprite {

public function EditableList() {
}

}

}

You will call an init() method from within the constructor to set up event listeners on all your components. Add the lines in bold to the EditableList class:

package com.foundationAS3.ch9 {

import fl.controls.Button;
import fl.controls.List;
import fl.controls.TextInput;

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

public class EditableList extends Sprite {

public function EditableList() {
init();
}

private function init():void {
addName_bn.addEventListener(MouseEvent.CLICK, onAddName);
deleteName_bn.addEventListener(MouseEvent.CLICK, onDeleteName);
editName_ti.addEventListener(Event.CHANGE, onNameChange);
names_li.addEventListener(Event.CHANGE, onNameSelected);
}
private function onAddName(event:MouseEvent):void {
}

private function onDeleteName(event:MouseEvent):void {
}

private function onNameChange(event:Event):void {
}

private function onNameSelected(event:Event):void {
}

}

}

Because all the controls components are interactive display objects, they have the same event API that you have explored previously with Sprite. Each component may have additional events that are dispatched based on the type of component. For instance, the CHANGE event is dispatched from a List instance whenever the selection in the List instance changes. You will use this CHANGE event to update the other controls based on the selection. The CHANGE event is also dispatched from a TextInput instance when its text is changed. You will use this event from editName_ti to update the data in the List instance for the selected item.

First, though, you need to provide a way to enter items into the list. You will handle this in the onAddName() method, which will be called whenever addName_bn is clicked. Update the onAddName() method with the following bold lines.

private function onAddName(event:MouseEvent):void {
var newItem:Object = {label:addName_ti.text};
names_li.dataProvider.addItem(newItem);
addName_ti.text = "";
}

A List instance has a dataProvider property through which all manipulation of the internal list data must be done. You can use the addItem() method of dataProvider to append items to the List instance’s data. A new item needs at least a label property for display in the list, so you add a new item by creating an Object instance with one property: label. To this label property, you assign the value of the addName_ti instance’s text property. Once addItem() has been called, you remove the text from addName_ti so that the user can enter a new item.

If you test your movie now and type a name in the bottom field and click the add button, that name should appear in names_li. Try adding several names to see them appear.

Next, let’s allow someone to select an item in the list and be able to edit or remove that item. Removing items will be handled in onDeleteName(). Editing items will be handled in the onNameChange(). To make it easy for a user to edit a name, you’ll populate the editName_ti TextInput instance with the label value of the currently selected item in the list. That will be taken care of in the ­onNameSelected() handler. Update your EditableList class with the following bold lines (code not listed does not need to be changed):

private function onDeleteName(event:MouseEvent):void {
names_li.dataProvider.removeItemAt(names_li.selectedIndex);
editName_ti.text = "";
}

private function onNameChange(event:Event):void {
var newItem:Object = {label:editName_ti.text};
names_li.dataProvider.replaceItemAtimage
(newItem, names_li.selectedIndex);
}

private function onNameSelected(event:Event):void {
editName_ti.text = names_li.selectedItem.label;
}

When an item is selected in the list, the onNameSelected() handler will be called. This assigns the label from the currently selected item, found through the aptly named selectedItem property of List, to the text property of editName_ti.

If the user then types into editName_ti, adding or removing letters, the onNameChange() handler will be called (thanks to the CHANGE event fired by editName_ti). A new item is created using the current text in editName_ti. You then can use the list dataProvider’sreplaceItem() method to replace the value of the currently selected item with the new updated item.

Finally, whenever the deleteName_bn button is clicked, it will dispatch a CLICK event, which will be handled by the onDeleteName() method. The dataProvider’s removeItemAt() method is just what you need to remove the currently selected item. removeItemAt()takes an integer value representing the index of the item in the list to remove, which you can obtain through the selectedIndex property of List. Once the item is deleted, you remove the text from the editName_ti field as well. ActionScript:

At this point, you have a pretty nice little miniapplication to create lists. One problem, though, is that the delete button can be clicked when no item has been selected. The same issue occurs with the editName_ti component. Also, items can be added to the list without any labels by clicking the add button without typing anything into addName_ti. You can take care of these problems by enabling and disabling controls based on the states of other controls. All the controls have an enabled property, which actually comes from the UIComponent class from which they all descend. If this property is not true, then the component does not allow for interaction.

First, you want to disable the two buttons and editName_ti when the SWF launches. This will ensure that the only initial interaction allowed will be entering text into addName_ti. Add the following bold lines to the init() method to try this:

private function init():void {
addName_bn.addEventListener(MouseEvent.CLICK, onAddName);
addName_ti.addEventListener(Event.CHANGE, onNameEnter);
deleteName_bn.addEventListener(MouseEvent.CLICK, onDeleteName);
editName_ti.addEventListener(Event.CHANGE, onNameChange);
names_li.addEventListener(Event.CHANGE, onNameSelected);

addName_bn.enabled = false;
deleteName_bn.enabled = false;
editName_ti.enabled = false;
}

Unfortunately, if you test the movie, you will see that the buttons don’t disable themselves as expected, although the text input field does. Well, that’s no good. This is a problem with the constructor running before some internal code within the component, which is re-enabling itself. To handle this, you will move your code into the next frame by setting up a handler for the ENTER_FRAME event and disabling the components then. Alter your code for the init() method to look like the following, which creates a new listener for the ENTER_FRAME event and moves the disabling code into that listener:

private function init():void {
addName_bn.addEventListener(MouseEvent.CLICK, onAddName);
addName_ti.addEventListener(Event.CHANGE, onNameEnter);
deleteName_bn.addEventListener(MouseEvent.CLICK, onDeleteName);
editName_ti.addEventListener(Event.CHANGE, onNameChange);
names_li.addEventListener(Event.CHANGE, onNameSelected);
addEventListener(Event.ENTER_FRAME, onNextFrame);
}

private function onNextFrame(event:Event):void {
removeEventListener(Event.ENTER_FRAME, onNextFrame);
addName_bn.enabled = false;
deleteName_bn.enabled = false;
editName_ti.enabled = false;
}

If you test the movie again, you should see that the buttons are disabled. Now you need to handle when to enable those buttons.

The add button should be enabled whenever there is text entered into the addName_ti field. You can listen for this through the CHANGE event for the TextInput instance. Then the button should disable itself when the field is cleared. Add the following bold lines to handle these events:

private function init():void {
addName_bn.addEventListener(MouseEvent.CLICK, onAddName);
addName_ti.addEventListener(Event.CHANGE, onNameEnter);
deleteName_bn.addEventListener(MouseEvent.CLICK, onDeleteName);
editName_ti.addEventListener(Event.CHANGE, onNameChange);
names_li.addEventListener(Event.CHANGE, onNameSelected);
addEventListener(Event.ENTER_FRAME, onNextFrame);
}

private function onNextFrame(event:Event):void {
removeEventListener(Event.ENTER_FRAME, onNextFrame);
addName_bn.enabled = false;
deleteName_bn.enabled = false;
editName_ti.enabled = false;
}

private function onAddName(event:MouseEvent):void {
var newItem:Object = {label:addName_ti.text};
names_li.dataProvider.addItem(newItem);
addName_ti.text = "";
addName_bn.enabled = false;
}

private function onNameEnter(event:Event):void {
addName_bn.enabled = addName_ti.text.length > 0;
}

private function onDeleteName(event:MouseEvent):void {
names_li.dataProvider.removeItemAt(names_li.selectedIndex);
editName_ti.text = "";
}

Now the add button will be enabled in the onNameEnter() method, which will be called when text is entered or edited in the addName_ti component. It will be disabled in the onAddName() method when a new item is added to the list and the addName_ti text is cleared.

The final step is enabling the delete button and the editName_ti component whenever an item can be edited or deleted. This will occur in the onNameSelected() handler. When an item is deleted, you need to disable these controls once again. Add the following bold lines, which should handle these occurrences:

private function onDeleteName(event:MouseEvent):void {
names_li.dataProvider.removeItemAt(names_li.selectedIndex);
deleteName_bn.enabled = false;
editName_ti.text = "";
editName_ti.enabled = false;
}

private function onNameChange(event:Event):void {
var newItem:Object = {label:editName_ti.text};
names_li.dataProvider.replaceItemAt(newItem, image
names_li.selectedIndex);
}

private function onNameSelected(event:Event):void {
editName_ti.text = names_li.selectedItem.label;
editName_ti.enabled = true;
deleteName_bn.enabled = true;
}

Save the EditableList.as file and return to the editableList.fla file.

You now need to set the document class for this file to be the EditableList class you just created. In the Property inspector, set the document class to com.foundationAS3.ch9.EditableList.

Now test your movie. You should see your miniapplication for creating and editing a list of names (as shown earlier in Figure 9-21), all through the use of Flash components and ActionScript to wire everything together.

If you get errors when compiling, see whether the error messages help you determine where the syntax of what you typed was incorrect. If you are unable to debug, compare your file with the EditableList.as file included with the files you downloaded for this chapter.

That’s not a bad little app for a few pages’ work! Imagine now if you had to code all those components yourself to build such an application, and how much longer such a task would take. There are times when that will be necessary and inevitable, but for the times it isn’t, the components offer a great way to quickly implement an application consisting of UI controls.

Styling and Skinning

One important thing to know about any component framework is how you might go about customizing the built-in components so that you can make the interfaces you create unique, with your own desired look and feel. Generally, altering items such as colors and fonts within a component is referred to as styling, and changing the graphics used to produce the overall look of a component is called skinning. Let’s look at what you can do within the Flash component framework to restyle and reskin for your needs.

Styling Components

With the V2 components for ActionScript 2.0 from previous versions of Flash, the default skins had a large number of styles that could be set to control colors within the skins. This offered great color configurability for the default skins. However, many found the task of reskinning to be difficult, or at least not as easy as they would have liked.

With the ActionScript 3.0 components, the workflow for reskinning has been greatly simplified. This simplification has also resulted in the reduction of the number of styles that can be set for the default skins. Although the configurable styles vary from component to component, most styles can be generally separated into those for setting text properties and those for setting skin properties. Using styles to set individual colors within a skin is no longer supported; instead, you need to create new skins with the desired new colors. You’ll learn how to create skins a little later in this chapter.

For now, let’s concentrate on altering text properties across components using styles. You’ll continue with the previous list example and change aspects of the text properties in order to achieve a slightly different look. To do this, you’ll change styles globally for all components, change styles for all instances of a single component class, and change the style on a single component instance.

Setting up for Changing Styles

To begin, save the class file EditableList.as as StyledList.as in the same directory. You’ll use this as a base file for testing your styling code. Also save the editableList.fla as styledList.fla and change the document class to StyledList. In StyledList, change the following bold code:

package com.foundationAS3.ch9 {

import fl.controls.Button;
import fl.controls.List;
import fl.controls.TextInput;

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

public class StyledList extends Sprite {

public function StyledList() {
init();
}

You’ll create a new method called setStyles(), which you’ll call from the init() method. To make things cleaner, you’ll move all the event listener code into its own method as well:

public function StyledList() {
init();
}

private function init():void {
setStyles();
addListeners();
}

private function setStyles():void {
}

private function addListeners():void {
addName_bn.addEventListener(MouseEvent.CLICK, onAddName);
addName_ti.addEventListener(Event.CHANGE, onNameEnter);
deleteName_bn.addEventListener(MouseEvent.CLICK, onDeleteName);
editName_ti.addEventListener(Event.CHANGE, onNameChange);
names_li.addEventListener(Event.CHANGE, onNameSelected);
addEventListener(Event.ENTER_FRAME, onNextFrame);
}

To set styles for all components or all instances of a certain class, you need to use the Flash component framework’s StyleManager class. Import this class at the top of the code, along with TextFormat, which you’ll need for setting text formats for components:

package com.foundationAS3.ch9 {

import fl.controls.Button;
import fl.controls.List;
import fl.controls.TextInput;
import fl.managers.StyleManager;

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextFormat;

public class StyledList extends Sprite {

Now you can start setting some styles!

Setting Styles for all Components

To set styles for all components in the framework, use the StyleManager’s setStyles() method. This takes the name of the style property to set and the value for that property. Let’s first change the color of the text for all components to a blue to match the highlight color built into the skins. Add the lines in bold to achieve this:

private function setStyles():void {
// blue green text for all components' default state
var format:TextFormat = new TextFormat("Arial", 10, 0x01578F);
StyleManager.setStyle("textFormat", format);
}

If you test your movie now, you should see blue text for all the components, except for the disabled state of the buttons, which requires setting a different property: disabledTextFormat.

Setting a Style for all Instances of a Component

Next, let’s try changing a style for all instances of a single component. For this, you will use StyleManager’s setComponentStyle() method, which takes the class of component for which you wish to set a style property, the name of the property, and the value for that property. Because you’re using only plus and minus symbols for your buttons, let’s boost up the point size of the text to make it more visible. For this, add the following lines in bold:

private function setStyles():void {
// blue green text for all components' default state
var format:TextFormat = new TextFormat("Arial", 10, 0x01578F);
StyleManager.setStyle("textFormat", format);
// blue green text for Button default state
format = new TextFormat("Arial", 12, 0x01578F);
StyleManager.setComponentStyle(Button, "textFormat", format);
// light gray text for Button disabled state
format = new TextFormat("Arial", 12, 0x999999);
StyleManager.setComponentStyleimage
(Button, "disabledTextFormat", format);
}

You set two properties here, textFormat and disabledTextFormat, so that the point size of the text doesn’t change based on the state. Notice that you can assign a new TextFormat instance each time to the format variable after the previous value has been passed in thesetComponentStyle() call. You can do this because the variable is simply storing a reference, and that reference is passed in the method call, not the variable itself. So you then can assign new references to the variable that can be passed in the subsequent calls. If you test the movie again, the text for the button labels should be increased by two points.

Setting the Style for a Single Instance of a Component

As a final step, let’s change the color of just the deleteName_bn component so that its label is red when it is clickable, giving a bit of a visual warning that clicking the button causes a destructive action. To set the style on a single instance, you need to use the UIComponent class’ssetStyle() method, which, like StyleManager’s setStyles(), takes a name of a style property and the value to set. Because all the controls inherit from UIComponent, this method is available on each component instance. Add the following lines in bold:

private function setStyles():void {
// blue green text for all components' default state
var format:TextFormat = new TextFormat("Arial", 10, 0x01578F);
StyleManager.setStyle("textFormat", format);
// blue green text for Button default state
format = new TextFormat("Arial", 12, 0x01578F);
StyleManager.setComponentStyle(Button, "textFormat", format);
// light gray text for Button disabled state
format = new TextFormat("Arial", 12, 0x999999);
StyleManager.setComponentStyleimage
(Button, "disabledTextFormat", format);
// red text for delete button default state
format = new TextFormat("Arial", 12, 0xFF0000);
deleteName_bn.setStyle("textFormat", format);
}

If you test your movie once more, you’ll see that all components have blue text (unless disabled); the button instances have text at a slightly larger point size; and when the delete button is active, its label is red as opposed to blue. This is styling at work!

Skinning using the Timeline

If you are used to the graphic tools in Flash, reskinning a component using the new ActionScript 3.0 components is a piece of cake—simpler than it has been in either of the two previous versions. Any component instance on the stage or symbol in the Library allows you to double-click it to enter into symbol-editing mode, in which the different supported skins are made obvious. Figure 9-22 shows an example of entering symbol-editing mode by ­double-clicking the List symbol.

image

Figure 9-22. The skins that can be found within the List symbol

Entering symbol-editing mode for a component immediately takes you to frame 2 of the component’s timeline, where graphic skins on the left are labeled with descriptive text on the right. If you want to edit any of the skins, double-click the desired skin to enter its symbol-editing mode.Figure 9-23 shows what you find if you enter symbol-editing mode by double-clicking List_Skin within the List symbol.

image

Figure 9-23. List_Skin, which is the back graphic of the List component, in symbol-editing mode

At this point within the List_Skin symbol, you have a simple vector graphic that you can edit directly to change shapes, colors, or both; and these changes will be reflected in every single List instance in your application. The horizontal and vertical dotted lines represent the slices for the scale9 grid. This grid determines how a graphic is resized, and it is a great enhancement over manually slicing components, as we had to do before the feature was introduced in Flash 8. Only the middle section of the grid is scaled both vertically and horizontally. The top and bottom rows are not scaled vertically, and the right and left columns are not scaled horizontally. For more information about scale9 grids and how to create and use them, check out the Flash documentation.

Sometimes, a complex component contains nested components, which you need to navigate to in order to find the vectors to edit them. If you return to symbol-editing mode for the List symbol and double-click ScrollBar Skins, you should see something like Figure 9-24.

image

Figure 9-24. The ScrollBar skin accessed from within the List component

ScrollBar is a complex symbol that contains a number of nested skins. You need to be aware that multiple components use the ScrollBar symbol, so although you might be editing the ScrollBar symbol from within the List symbol, any component that uses these skins will be affected. The same holds true for editing of a global skin such as the Focus Rect Skin, which appears within every single component. Editing this changes the skin that is used by all components that allow focus.

Let’s do some timeline graphic editing to see how easy it is to change a skin in this manner. You will change the graphics for the TextInput component so it uses rounded corners like the Button component, as opposed to sharp corners. You’ll need to edit both the enabled and disabled states.

1. Save styledList.fla as timeSkinsList.fla. The document class can remain the same because you will not alter any code for this example; you will change only the Library graphics.

2. Double-click one of the TextInput instances on the main timeline’s stage. You should enter symbol-editing mode and see the skins, as shown in Figure 9-25.

image

Figure 9-25. Symbol-editing mode for TextInput

3. Double-click the Normal Skin graphic (TextInput_upSkin) to enter its symbol-editing mode. You should see a graphic like the one shown in Figure 9-26.

image

Figure 9-26. Symbol-editing mode for the Normal Skin graphic within TextInput

4. Delete the graphics on the timeline and draw a rounded rectangle with a corner radius of 3 and a stroke of 1 pixel that is around 150 × 25 in its dimensions. You do not need to be exactly precise, as you can drag the scale9 dividers wherever they need to go to suit your graphic.

5. Make the fill of the rectangle white (#FFFFFF). Assign a linear gradient to the stroke with two color stops set to #6D6F70 and #D3D5D6. Use the Gradient Transform tool to rotate and scale the gradient so that it runs vertically down the component, moving from the darker color to the lighter color. The result should look like Figure 9-27.

image

Figure 9-27. A new graphic is drawn for the skin using a rounded rectangle

6. Use the Selection tool to move the scale9 grid dividers to make sure that the corners do not get scaled in any resizing of the component; only the straight lines scale on resizing.

7. Now that the Normal Skin is complete, copy it to your clipboard.

8. Return to symbol-editing mode for the TextInput symbol. Double-click the Disabled Skin graphic (TextInput_disabledSkin) to enter its symbol-editing mode. Delete the graphic that is on its timeline and then paste in place the graphic copied from theNormal Skin.

9. Alter the fill for the disabled state rectangle to #DBDBDB. Alter the gradient stroke so that the top color is #AFAFB0 and the bottom color is #CECECF.

Now test your movie. You should see nice rounded corners on the TextInput instances, in both the normal and disabled states.

Skinning using Classes

Now it just doesn’t seem right that we spend too much time in an ActionScript book doing edits in the timeline when we have this perfectly wonderful language to accomplish the same thing. Let’s take a look at how you might use code to create and set skins for components, as opposed to editing graphic symbols in the Library.

First, it helps to remember that any symbol exported from the Library is done so as a class, whether you specify the class file to associate with the symbol or it is generated for you automatically behind the scenes. As such, any skin that is in the Library that a component instantiates at runtime is a class. There is nothing that requires that such a skin class needs to be in the Library, as long as it is available for a component to instantiate when it needs to draw itself. Therefore, you do not need to use predrawn graphics in the Library, but can draw the graphics through code in a custom class, as long you specify which class or classes a component should use for its skins.

In this next example, you’ll create a new skin for the buttons in your list application, but instead of using the vector tools on the timeline, you’ll create the graphics using ActionScript. The skins will be simple round graphics with a one-point stroke. You’ll create a different skin for each of the four states of the buttons: up, over, down, and disabled.

To begin, create a new ActionScript file and save it as StrokedCircle.as into a new skins subdirectory in the com/foundationAS3/ch9 directory (so the full path will be com/foundationAS3/ch9/skins).

Creating a Skin for the Button’s Up State

Next, enter the following code, which represents everything you will need to draw the up state for your buttons:

package com.foundationAS3.ch9.skins {

import flash.display.Shape;

public class StrokedCircle extends Shape {

protected var _fillColor:uint = 0xE6E6E6;
protected var _strokeColor:uint = 0x5C5C5C;

public function StrokedCircle() {
init();
}

protected function init():void {
draw();
}

private function draw():void {
graphics.lineStyle(1, _strokeColor);
graphics.beginFill(_fillColor);
graphics.drawCircle(25, 25, 25);
graphics.endFill();
}

}

}

At this point in the book, nothing here should come as a surprise to you. The class extends Shape because it is merely drawing graphics into itself and does not need to allow for interactivity. You define two protected properties for the fill and stroke color. They are made protected so that child classes can set their own values for these colors. The constructor for the class simply calls the init() method, which is also made protected so that child classes can use it to set new values for the colors before the graphics are drawn, which is handled in the draw() method. Thatdraw() method sets a line style of 1 point and draws a circle with a 25-pixel radius, which is really arbitrary because the components will resize the skin as necessary by setting its width and height directly.

That is all you need to do to create a skin. Pretty easy, isn’t it? If you think so, then you’ll love the next steps. You need to create variations for the over, down, and disabled states, but you can use inheritance to leverage the drawing code you just wrote, and just have the child classes set new color values.

Creating Skins for the Button’s Other States

Create a new ActionScript file and save it as StrokedCircleOver.as into the same skins directory as StrokedCircle.as. Enter the following code:

package com.foundationAS3.ch9.skins {

public class StrokedCircleOver extends StrokedCircle {

override protected function init():void {
_strokeColor = 0x0076C1;
super.init();
}

}

}

Do you love it? A child class of StrokedCircle with a change of color needs to override only the superclass’s protected init() method (remember that it is the protected access setting that lets a child class override the parent class’s implementation). A new color is specified for the stroke color and then the superclass’s init() method is called. And that’s it—the same skin shape with different colors.

For the down state of the button, create another new ActionScript file and save it into the same skins directory as the other two skins as StrokedCircleDown.as. Enter the following code:

package com.foundationAS3.ch9.skins {

public class StrokedCircleDown extends StrokedCircle {

override protected function init():void {
_fillColor = 0xA7DCFE;
_strokeColor = 0x0076C1;
super.init();
}

}

}

This is pretty much the same as the StrokedCircleOver class, except that both the fill and stroke color are overridden in this case.

The final skin is for the disabled state of the button. Once more, create a new ActionScript file and save it into the skins directory as StrokedCircleDisabled.as. The following is the entire class code:

package com.foundationAS3.ch9.skins {

public class StrokedCircleDisabled extends StrokedCircle {

override protected function init():void {
_fillColor = 0xE8E8E8;
_strokeColor = 0xC2C3C5;
super.init();
}

}

}

At this point, you have created four new skins for your buttons in separate class files. The skins all inherit from Shape, because they are only containers for drawn graphics and don’t require any child objects or interactivity. All you need to do now is associate the classes with the relevant skin properties of the Button class.

Associating the Skins with the Buttons

Save the StyledList.as document class file created earlier as SkinnedList.as into the same directory. Let’s first update the file with the new class name. You’ll also import the skins package you just created, as you will need to reference the classes within the package to assign these classes to your Button instances:

package com.foundationAS3.ch9 {

import fl.controls.Button;
import fl.controls.List;
import fl.controls.TextInput;
import fl.managers.StyleManager;

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextFormat;

import com.foundationAS3.ch9.skins.*;

public class SkinnedList extends Sprite {

public function SkinnedList() {
init();
}

All the rest of the code should remain unchanged.

Now you need to associate the skins you just created in the class files with the properties of the Button class that determine which skin is used for which state. Just as with the text properties discussed earlier, this is all handled through the StyleManager and itssetComponentStyle() method, if you want to set skins for all instances of a component. If you want to change the skin on only a single instance, you use UIComponent’s setStyle(). In this case, you will change the skins for all Button instances.

Within the setStyles() method of SkinnedList, add the following bold lines to associate the new skin classes with the Button class:

private function setStyles():void {
// blue green text for all components' default state
var format:TextFormat = new TextFormat("Arial", 10, 0x01578F);
StyleManager.setStyle("textFormat", format);
// blue green text for Button default state
format = new TextFormat("Arial", 14, 0x01578F);
StyleManager.setComponentStyle(Button, "textFormat", format);
// light gray text for Button disabled state
format = new TextFormat("Arial", 14, 0x999999);
StyleManager.setComponentStyleimage
(Button, "disabledTextFormat", format);
// red text for delete button default state
format = new TextFormat("Arial", 14, 0xFF0000);
deleteName_bn.setStyle("textFormat", format);
StyleManager.setComponentStyle(Button, "upSkin", StrokedCircle);
StyleManager.setComponentStyleimage
(Button, "overSkin", StrokedCircleOver);
StyleManager.setComponentStyleimage
(Button, "downSkin", StrokedCircleDown);
StyleManager.setComponentStyle(Button,image
"disabledSkin", StrokedCircleDisabled);
}

Each component class has different skin properties depending on the type and complexity of the component. The Button class itself has many more skin properties, but some of these deal only with Button instances that toggle or with icons. The four skin properties that define the four different states are all that you need to modify here. As you can see, the value for each skin property is actually a reference to the class itself that should be used to draw the button in that state.

Return to the SytledList.fla file from earlier in the chapter and in the Property inspector change the Document Class to reflect the new SkinnedList.as file.

If you test your movie now, you should see that the buttons have changed their appearance to a stroked circle with a solid fill, as shown in Figure 9-28. Using the StyleManager and a little ActionScript in the manner just demonstrated, it’s easy to reskin your applications for each project.

image

Figure 9-28. The result of using ActionScript to reskin all Button instances

Creating Components from Scratch

Sometimes you will need functionality that isn’t provided by the default Flash components, and your best course of action may be to create a component specifically for your purpose. Creating a component is basically like creating a custom class, with only a few extra steps to plug the class into the Parameters panel so that its configurable parameters appear. This next example will demonstrate the process as you create a simple UI component for a dial interaction.

Creating the Widget

Let’s start by creating the symbol and graphics needed for the component. Remember that the whole purpose of a component is to allow for a visual widget that you can position and configure in the Flash IDE; otherwise, you could do the same without components using ActionScript alone. So creating the visual widget is a good place to begin.

1. Create a new Flash ActionScript 3.0 file and save it as dial.fla into your Chapter 9 project directory.

2. On the stage, draw a 30 × 30-pixel gray circle with no stroke. Use the Line tool to draw a 1-point line that is 15 pixels long. Align the line so that it is centered vertically within the circle and aligned to its right edge. The result should look like Figure 9-29.

image

Figure 9-29. The dial graphic created from a circle and a line

3. Select both the circle and line, and convert them into a movie clip symbol (press F8). Name it Dial_skin and make sure it is aligned in the center, as shown in Figure 9-30. Click OK.

image

Figure 9-30. Creating a symbol from the graphics for the dial component

4. In the Property inspector, name the Dial_skin instance still selected on the stage graphic. Convert this instance into a movie clip symbol as well (press F8 again), but this time, align the symbol in the top left and name it Dial. Select to export the symbol for ActionScript, but set its class to be com.foundationAS3.ch9.controls.Dial, as shown in Figure 9-31. Click OK. You will see an ActionScript Class Warning dialog box letting you know that there is no class found, but this is OK because you will create the class in the next section.

image

Figure 9-31. The Dial symbol is created for the dial component

Writing the Component Code

Create a new ActionScript file and save it as Dial.as into a new controls subdirectory in the com/foundationAS3/ch9 directory, so the full path is com/foundationAS3/ch9/controls. Add the following code to start the class. This adds all the imports you will need, including Sprite, from which the Dial class inherits:

package com.foundationAS3.ch9.controls {

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

public class Dial extends Sprite {

public function Dial() {
}

}

}

As mentioned previously, the Flash ActionScript 3.0 UI components all extend, either directly or indirectly, the UIComponent class (the FLVPlayback component, however, does not). Certainly, if you want to take advantage of the framework and what it offers, such as focus management and styling, you would want to extend UIComponent. But that requires a bit more work because you need to build the component in particular ways and override certain methods. To keep things simple for this example, you will just extend Sprite, which is also perfectly valid.

First, you need to call an init() method from your constructor, as you have often done in classes in previous examples. You know that you have a graphic on the timeline of the symbol, and you will need to manipulate that graphic. You could do what you have done previously, and just code against the instance name graphic, but you would lose helpful code hints. So you will assign a reference to this graphic and cast that to Sprite for easier development. You need to use getChildByName(), because referencing the graphic directly would throw an error when turning your symbol into a component.

public class Dial extends Sprite {

private var _graphic:Sprite;

public function Dial() {
init();
}

private function init():void {
_graphic = graphic;
}

Now you can code using the private property _graphic and get those handy-dandy code hints as you type.

Next, you should handle the interaction with the dial.

Handling Events

When users click the dial, they should be able to then rotate it. The obvious event to listen for is the MOUSE_EVENT that is fired when users click the dial. Add the following bold lines to handle this event:

private function init():void {
_graphic = graphic;
addEventListener(MouseEvent.MOUSE_DOWN, onClickDial);
}

private function onClickDial(event:MouseEvent):void {
}

Whenever the dial is clicked, the onClickDial() handler will run. Two things will need to happen after this point. First because the user moves the mouse, the dial should rotate. Second, when the user releases the mouse, the dial should stop rotating. You can handle the former with the MOUSE_MOVE event, and set up a listener for this only after the user has clicked the dial. The second item is a little trickier because you want to not only handle when the user releases the mouse while over the dial, but also if the user moves the mouse off the dial and releases. Adding a listener to the MOUSE_UP event fired by the dial is not enough to achieve this because that does not account for whether the mouse is not over the dial when released. In order to account for both circumstances, you can listen to the stage’s MOUSE_UP event, not the dial’s.

Because MOUSE_UP is a bubbling event, eventually this event will reach the stage from any display object that causes it to be dispatched. Because of this, you can safely subscribe to this event to catch whenever the mouse is released, but you want to do this only if the user has first clicked the dial. So you’ll add the listener only in the onClickDial() method. Add the following lines in bold to handle the mouse move and release events after the dial has been clicked:

private function onClickDial(event:MouseEvent):void {
addEventListener(MouseEvent.MOUSE_MOVE, onRotateDial);
stage.addEventListener(MouseEvent.MOUSE_UP, onReleaseDial);
}

private function onReleaseDial(event:MouseEvent):void {
removeEventListener(MouseEvent.MOUSE_MOVE, onRotateDial);
stage.removeEventListener(MouseEvent.MOUSE_UP, onReleaseDial);
}

private function onRotateDial(event:MouseEvent):void {
}

Now the flow is that when the user clicks the dial, onClickDial() is called. Two listeners are added to listen for when the mouse moves or when the mouse is released. When the mouse moves, ­onRotateDial() is called, where you’ll handle rotating the graphic. When the mouse is released anywhere on the stage, onReleaseDial() is called, and the mouse move and released listeners are removed.

You have taken care of the events. Now you need to rotate the dial as the user moves the mouse.

Rotating the Dial

In order to rotate the dial with mouse movement, you need to record the starting angle of the dial when it is first clicked. As the user moves the mouse, you can measure the angle of the mouse in relation to the center of the graphic and rotate the graphic accordingly, adjusting for the starting angle. The lines in bold take care of this:

public class Dial extends Sprite {

private var _graphic:Sprite;
private var _startRadians:Number;
private var _startRotation:Number;

public function Dial() {
init();
}

private function init():void {
_graphic = graphic;
addEventListener(MouseEvent.MOUSE_DOWN, onClickDial);
}

private function onClickDial(event:MouseEvent):void {
_startRotation = _graphic.rotation;
var click:Point =image
new Point(mouseX-_graphic.x, mouseY-_graphic.y);
_startRadians = Math.atan2(click.y, click.x);
addEventListener(MouseEvent.MOUSE_MOVE, onRotateDial);
stage.addEventListener(MouseEvent.MOUSE_UP, onReleaseDial);
}

private function onReleaseDial(event:MouseEvent):void {
removeEventListener(MouseEvent.MOUSE_MOVE, onRotateDial);
stage.removeEventListener(MouseEvent.MOUSE_UP, onReleaseDial);
}

private function onRotateDial(event:MouseEvent):void {
var distance:Point =image
new Point(mouseX-_graphic.x, mouseY-_graphic.y);
var radians:Number =image
Math.atan2(distance.y, distance.x) - _startRadians;
var degrees:Number = radians*180/Math.PI;
_graphic.rotation = _startRotation + degrees;
}

Here, you save the starting rotation of the graphic in _startRotation. You also find the angle of the mouse click in relation to the center of the graphic using the Math.atan2() method, saving the results into _startRadians. The Math.atan2() method takes the distance on the y axis and the distance on the x axis, and determines the angle in radians based on these values.

Whenever the mouse moves after the dial is clicked, you find the current distance from the center of the graphic and convert this to an angle measured in radians as well, subtracting from this the starting radians value. At this point, you have an angle that measures the distance the mouse has moved from its initial click. You can convert this to degrees using the standard formula: degrees = radians × 180/PI. It is this degrees value that you can add to the initial starting rotation of the graphic to set a new rotation for the graphic based on the mouse position.

At this point, you have a working dial that can be clicked and turned, but it doesn’t have much use unless you can know what the current value of the dial is and when it changes. Your next step is to set up a way to determine when the value changes and provide access to that value.

Getting and Setting Dial Values

The value of the dial itself can easily and simply be the current rotation of the graphic, but to make the component useful in multiple environments, let’s allow for a minimum and maximum value to be set.

Add the following new properties at the top of your code with the other property declarations:

public class Dial extends Sprite {

private var _graphic:Sprite;
private var _startRadians:Number;
private var _startRotation:Number;
private var _minValue:Number = 0;
private var _maxValue:Number = 100;

public function Dial() {
init();
}

Next, let’s add getters and setters for these values at the bottom of the class definition:

private function onRotateDial(event:MouseEvent):void {
var distance:Point =image
new Point(mouseX-_graphic.x, mouseY-_graphic.y);
var radians:Number =image
Math.atan2(distance.y, distance.x) - _startRadians;
var degrees:Number = radians*180/Math.PI;
_graphic.rotation = _startRotation + degrees;
}

public function get minValue():Number {
return _minValue;
}

public function set minValue(min:Number):void {
_minValue = min;
}

public function get maxValue():Number {
return _maxValue;
}

public function set maxValue(max:Number):void {
_maxValue = max;
}

}

}

Now, based on the minimum and maximum values (which you’ve defaulted to 0 and 100, respectively), you can provide a getter for the current value based on the current rotation of the graphic within the specified numeric range.

Add the following getter and setter for the current value of the dial:

private function onRotateDial(event:MouseEvent):void {
var distance:Point = image
new Point(mouseX-_graphic.x, mouseY-_graphic.y);
var radians:Number = image
Math.atan2(distance.y, distance.x) - _startRadians;
var degrees:Number = radians*180/Math.PI;
_graphic.rotation = _startRotation + degrees;
}

public function get value():Number {
var degrees:Number = _graphic.rotation;
if (degrees < 0) degrees += 360;
return degrees/360*(_maxValue-_minValue)+_minValue;
}

public function set value(num:Number):void {
_graphic.rotation = (num-_minValue)/(_maxValue-_minValue)*360;
}

public function get minValue():Number {
return _minValue;
}

public function set minValue(min:Number):void {
_minValue = min;
}

When the getter is called, you find the current rotation of the _graphic property, making it positive if it is negative (the rotation will fall between –180 and 180). You can then find where this lies as a percentage of 360 degrees. This percentage can be multiplied with the full range (maximum value minus the minimum value). If the minimum value is greater than 0, you can add this to your result.

Let’s plug in some numbers to see how these equations work. If your minimum value is 0 and your maximum value is 100, and your dial is rotated exactly 180 degrees (halfway), then 180/360 will give you 0.5. This value multiplied by the full range (100 – 0 = 100) gives 50 (0.5 × 100). You add this to the minimum value (0), for a result of 50.

As another example, if the range is specified with a minimum of 100 and a maximum of 200, and the dial is rotated 120 degrees, you get a rotation percentage of roughly 0.33 (120/360), and this is multiplied by a range of 100 (200 – 100), which gives a result of around 33. This value added to the minimum value results in around 133.

When the setter for value is called, this formula is simply reversed.

When the value changes for the dial, you should dispatch an event so that listeners can be informed of the changes. You can take care of this in the onRotateDial() method. Add the following bold line to achieve this:

private function onRotateDial(event:MouseEvent):void {
var distance:Point =image
new Point(mouseX-_graphic.x, mouseY-_graphic.y);
var radians:Number =image
Math.atan2(distance.y, distance.x) - _startRadians;
var degrees:Number = radians*180/Math.PI;
_graphic.rotation = _startRotation + degrees;
dispatchEvent(new Event(Event.CHANGE));
}

You have now created a working dial that you could use in any project by instantiating it through the code and setting its properties. To make it into a component, you simply need to specify which of its properties should appear in the Parameters panel as configurable properties. You do this through the use of metatags in the ActionScript.

Adding Metatags

Metatags are special tags that are ignored by the compiler, but have special significance in the IDE. For components, there is an Inspectable tag that you can place in your ActionScript to inform the IDE of which properties should be accessible in the Component Inspector.

Add the following Inspectable metatags to your code:

public function get value():Number {
var degrees:Number = _graphic.rotation;
if (degrees < 0) degrees += 360;
return degrees/360*(_maxValue-_minValue)+_minValue;
}

[Inspectable(defaultValue=0)]
function set value(num:Number):void {
_graphic.rotation = (num-_minValue)/(_maxValue-_minValue)*360;
}

public function get minValue():Number {
return _minValue;
}

[Inspectable(defaultValue=0)]
public function set minValue(min:Number):void {
_minValue = min;
}

public function get maxValue():Number {
return _maxValue;
}

[Inspectable(defaultValue=100)]
public function set maxValue(max:Number):void {
_maxValue = max;
}

Not too tough, is it? The only additional piece of information that you provide in this example is the default value you would like to have set. To find out more about the use of the Inspectable tag and what it allows, consult the Adobe documentation.

Your code is complete at this point, so save it and return to the dial.fla file you created at the beginning of the example.

Turning the Symbol into a Component

To turn your Dial symbol into a component, all you need to do is right-click it in the Library and select Component Definition. This opens the Component Definition dialog box, as shown in Figure 9-32.

image

Figure 9-32. The Component Definition dialog box

In the dialog box, enter the name of your ActionScript class into the Class text box: com.foundationAS3.ch9.controls.Dial and then click OK. At this point, the symbol icon in the Library should change to the component symbol, as shown in Figure 9-33.

image

Figure 9-33. The Library gets a swanky new icon for the Dial component

It’s time now to test your component!

Testing the Component

Let’s try out the component in a little movie.

1. Select the Dial instance you have on the stage in dial.fla (if you have deleted it from the stage, simply drag a new instance from the Library) and look at the Parameters panel. You will see that your dial has three configurable parameters: maxValue,minValue, and value. You can keep the default values (or change them if you would like to see the result),

2. Name the instance of the component dial in the Property inspector.

3. Create a dynamic TextField instance next to the dial and name it dialValue. Turn on its background for better visibility.

4. Create a new layer on your timeline and select the first frame of this layer.

5. Open your Actions panel (press F9) and enter the following code.

dial.addEventListener(Event.CHANGE, onDialChange);
dialValue.text = String(Math.round(dial.value));

function onDialChange(event:Event):void {
dialValue.text = String(Math.round(dial.value));
}

6. Test your movie. You should see something like Figure 9-34. Turning the dial should update the number within the text field to show the dial’s current value.

image

Figure 9-34. The simple dial component in action

And that’s it for creating a component! Of course, you can do a lot more with it, such as limiting the range of movement and offering multiple states for rollover and disabled, but the basic functionality is there. You have seen how easy it is to turn a class into a component in your Library.

In this example, you’ve left this class uncompiled so that it is easy to go in and edit the graphic for a project. However, this simplified approach also means that you cannot have different skins for multiple dials in a single application. As explained earlier in the chapter, precompiling will also speed up compile times when you have many components. Finally, precompiling by turning a symbol into an SWC file automatically creates a Live Preview of your component. This wouldn’t affect the sample component, but if you had a number of parameters that affected visual properties, the Live Preview feature is a great asset.

This has given you a little taste of what it takes to put together a component, and perhaps you will be encouraged to explore more and build your own component libraries for your projects. The work is in initially creating the components, but after you have done it once, the reuse of the components and the speed components can bring to your development are fantastic advantages.

There can be a whole lot more behind a component than what you saw here. First, components can be built on top of the Flash component framework to help manage things like skins and focus management. Live Previews can be built or enabled by precompiling the class into an SWC, and custom interfaces for setting component parameters can be built and used instead of the Parameters panel, which has limitations because it requires simple form input. Finally, after a component is built, it can be distributed using the Adobe Extension Manager so that the component can be installed by any user acquiring the extension and will appear in the Component panel along with the default components. Check out the Flash documentation as well as the Flash Developer Center (http://www.adobe.com/devnet/flash/) for more information about these topics.

Using Third-Party Components

Of course, sometimes you just don’t have the time to create your own components. Thank goodness that someone else has probably already done it. Be sure to check out the Adobe Exchange (http://www.adobe.com/cfusion/exchange) for free components, as well as more complex components available for purchase.

A number of third-party sites also provide a wealth of components to shop through to see what might suit your needs, such as Flashloaded (http://www.flashloaded.com) and the Flash Components network (http://flashcomponents.net). Any of these sites, as well as the Flash documentation, should have instructions for installing the components. In any case, you will need the Adobe Extension Manager, which comes with the Flash CC installation. You can always check for updates at Adobe’s site.

Summary

The goal of this chapter was to give you some insight into the power and flexibility of using the Flash components, the speed at which you can build applications that incorporate the framework, and how you might configure these components to create unique experiences for your users. You’ve also looked at how you might create your own components for your own needs and acquire ready-made components from third parties. The use of components can greatly increase your rate of production and ease your workflow, while encouraging you to work in an object-oriented manner by separating and containing functionality in individual objects.

In the next chapter, we put the IDE aside once more and dive right back into the code, exploring a powerful new ActionScript feature: regular expressions. If you ever need to do any string manipulation or searching through text, regular expressions will prove to be your best friends. You might want to take a break and interact a bit with your human friends first, though. Then come on back, turn the page, and we’ll get into some really nifty programming.