An Excellent Tutorial on How to Create Custom Components with Action Script 2.0

Flash MX and MX 2004 include the v2 Flash component architecture and enable you to develop your own custom components.

Here’s an excellent article that goes through, step-by-step, on how to write your own class files, component movie clips and export to .swc.

Creating Components with Flash MX 2004

by Joey Lott (www.person13.com)

Don’t let the idea of writing a custom component intimidate
you. Although components may appear very complex, they are really just fancy
movie clips. You know how to work with movie clips, and so even if you don’t
yet know it, you also know how to write a custom component. There is only one
caveat: You do need to be familiar with writing ActionScript 2.0 classes. If
you don’t yet know how to write an ActionScript 2.0 class, then you’ll want
to make sure to read the ActionScript 2.0 primer series at www.person13.com/arcticles.

In this article we’ll discuss the following:

* Benefits of components.
* Creating a component movie clip.

*
Writing a basic component class.
* Linking a component class with a movie clip symbol.
* Exporting a compiled clip.

One thing that you may notice is not included in the preceding
list is a discussion of extending the v2 component framework. The much-hyped
v2 framework is not something I am going to discuss in this article for several
reasons. One reason is that I think you’ll find a benefit in understanding how
to create a component from scratch, without relying heavily on the architecture
built by Macromedia that can obfuscate the fundamental principals. Additionally,
I don’t find too much to champion about the v2 framework. While it definitely
provides some nice functionality such as style and focus management, it also
adds significant overhead in terms of kilobytes to your .swf. For example, if
you add the Macromedia ProgressBar component
to a Flash document and export the .swf, the file is 27KB. If you extend the
v2 framework, you can be assured that your components will also have a minimum
filesize that approximates that of the Macromedia ProgressBar.

I will note, however, that even if you do want to extend
the v2 framework, this article will still cover all the basic concepts that
you will want to know. Once you’ve understood the principals in this article
you will be prepared to then utilize the v2 framework.

Benefits of Components

Before you start learning how to create components, it is
likely beneficial to first investigate the potential benefits of components.
Although a component is really just a fancy movie clip, a component can be so
much more. It is an example of the whole being greater than the sum of its parts.

Consider the basic UI components included with Flash. You
could create a custom combobox or button control for each time you need one.
You could create the artwork and write a bunch of ActionScript code on the
main timeline, and accomplish something very similar. But it sure is a lot
simpler to just drag and drop and set a few parameters. That simple example
illustrates many of the benefits of components. Namely, the component contains
reusable functionality, encapsulated in such a way that you need only to drag
an instance onto the stage in order to utilize it. Additionally, components
allow you to set parameters either by way of a graphical interface (the Component
Inspector panel) or by way of an API. This means that with a component you
can still utilize the power of interfacing via ActionScript, but you also allow
for the flexibility of being able to just hand over the component to a developer
who is more comfortable setting parameters with the Component Inspector panel.

Not only are components encapsulated functionality, but
they also make distributing that functionality pretty simple. If you want to
let someone else use the component you’ve built, all you need to do is package
it, send it to them, and with a few clicks, they’ll have it installed. Furthermore,
if you package your component as an .swc file, then the code is relatively
protected (although typically some decompiler does exist that can reconstitute
the original ActionScript code.)

Creating a Component Movie Clip

With few exceptions you attach or load all artwork into a
component at runtime. That means that you will be handling just about everything
with ActionScript code. Therefore, a component movie clip is really quite simple.
However, there are a few things you need to keep in mind:

* A component will only actually render artwork that fits
within the boundaries of artwork that is defined within the movie clip at authoring
time. This might seem somewhat strange. What that means in practical terms is
that if you don’t define any authoring time artwork within the component movie
clip then none of the artwork you attach or load will be displayed. The solution
is to define a rectangular movie clip instance on the first frame of the timeline
within the movie clip symbol. I typically call the instance mcBoundingBox. And
mcBoundingBox should be made to the dimensions into which all the component
artwork will fit.

* In order to be able to correctly export an .swc file (which
you’ll see how to do a bit later) the component symbol needs to contain instances
of all other symbols that are attached via code. Otherwise the other symbols
are not exported into the .swc. Therefore, the suggested way to accomplish this
is to create a second frame in the component movie clip’s timeline, and add
instances of all the symbols you will attach with code.

* Since you’re component movie clip symbol will have two
frames – the first containing the mcBoundingBox instance, and the second containing
instances of all the other symbols you want to be able to attach – you should
make sure to place a stop() action on the first
frame.

Writing a Component Class

A component consists of at least one movie clip symbol and
an associated ActionScript class. You already know how to make movie clip symbols
(at least I assume you do) so what we want to discuss in this part of the article
is how to create the ActionScript class. If you’ve already learned how to write
a basic ActionScript 2.0 class, then this part is actually very simple.

Component classes should extend the MovieClip
class. By extending MovieClip, you ensure that
the component inherits all the properties and methods of the MovieClip
class. That means that you’ll be able to use ActionScript to do things like
control placement on the stage, set visibility and alpha, etc. It also means
that you can use methods like createEmptyMovieClip()
and attachMovie() within the class to add nested
elements to the component. Your class can extend MovieClip
either directly or indirectly. For example, you may want to create a base class
for your components that provides common functionality to all of them. That
class should then extend MovieClip, and all
your component classes should extend the base class.

There are a handful of special methods that component classes
can (and probably should) implement. We’ll take a look at some of those in a
bit. But first, let’s just create a basic class example.

class com.person13.SampleComponent extends MovieClip {


// You have to declare the class member in order to reference it within
// the code. In this case, mcBoundingBox is the name we have given to
// the authoring time bounding box movie clip that we created in the
// associated component symbol.

private var mcBoundingBox:MovieClip;



function SampleComponent() {
// It can be useful to have the constructor simply call other methods
// rather than placing a lot of code within it directly. This allows
// you to organize your code well. You'll see in later examples how
// this can be beneficial.

init();

}


private function init():Void {


// Typically you want to set the bounding box to be invisible and
// set its dimensions to 0. The bounding box's authoring time dimensions
// determine the renderable area within the component, but it can
// be hidden during runtime.
mcBoundingBox._visible = false;
mcBoundingBox._width = 0;
mcBoundingBox._height = 0;

}



}

The preceding class demonstrates the basics of a component
class. You should make sure to extend MovieClip
and hide the bounding box instance. The code thus far doesn’t do too much, but
I want to show you the code a piece at a time so that a) you don’t get overwhelmed
unnecessarily and b) you can see and understand what each part does.

As you can see indicated in the comments in the preceding
code, I recommend you call other methods from within the constructor rather
than placing a bunch of functionality within the constructor itself. I typically
use a set of methods that I call from within the constructor method in the following
order:

* init() – This is the method
within which any initialization code can appear. I typically scale the instance
too 100% for reasons you’ll read later. It’s also where I usually hide the bounding
box (as you’ve already seen) and initialize the class to be able to dispatch
events (something you’ll see more of in a short while.)

* createChildren() – This
is the method I use to attach and create all movie clips and text fields and
the like. It typically contains some attachMovie()
and/or createEmptyMovieClip() methods at the
very least.

* draw() – The draw()
method is one in which I programmatically draw anything that needs to be drawn.
Not all components necessarily need to draw anything. But for those that do,
the draw() method is where I place that code.

* arrange() – The arrange()
method is where I place the code that places the artwork at the correct coordinates.
Typically the arrange() method gets called at
different times throughout the component’s existence. Each time the component
is resized, for example, I’ll probably want to rearrange the elements.

You’ll get a chance to see more of all this kind of implementation
shortly.

Linking a component class with a movie clip symbol.

Once you’ve defined the basic movie clip symbol and the component
class, you next should associate them with one another. When an instance of
a movie clip is created – whether by dragging an instance onto the stage or
by attaching an instance with code – Flash typically associates the symbol with
the MovieClip class. But you want to tell Flash
to associate the symbol with your custom class so that the instances will be
able to access the functionality you have built into your class.

You can associate a class with a movie clip symbol by setting
the ActionScript 2.0 class value in the symbol’s linkage settings. To get to
the movie clip’s linkage settings right-click/command-click on the symbol in
the library, and choose Linkage. From the linkage settings you need to do several
things:

* Check the Export for ActionScript option.

* Give the symbol a linkage identifier.

* Specify the class in the AS 2.0 Class field. You must provide
the fully-qualified class name. That means that if the class is in the com.person13
package, for example, you need to include that package name.

You’ll also need to set the ActionScript 2.0 class value
within the symbol’s component definition. You can access the component definition
by right-clicking/command-clicking on the symbol in the library and choosing
the Component Definition option. Enter the fully-qualified class name in the
AS 2.0 Class field.

Once you’ve done those two things you have associated the
class with the symbol. Then, when you create an instance of the symbol Flash
will automatically make it an instance of the class instead of making it an
instance of MovieClip by default.

Adding Parameters

Although you may want to create components that can only
be controlled via their API, in most cases you will want to allow the component
instances to be configured from the Component Inspector panel – even by someone
who doesn’t know how to write any ActionScript code. That is one of the many
benefits of components.

If you created components in Flash MX then you will recall
that you defined parameters within the Component Definition dialog box. However,
that portion of the dialog box remains for legacy support only. When you create
a component in Flash MX 2004 with an ActionScript 2.0 class you should define
the parameters using special metatags within the class file. The metatag approach
has many benefits over the previous way of defining parameters. One of the biggest
advantages is that it is much more efficient than trying to add parameter values
in the silly dialog boxes like you used to have to do.

The [Inspectable] metatag
tells Flash to make a property a parameter for the component. If you place the
[Inspectable] metatag just before any property
(including getter and setter methods) within the class, that property will be
automatically made into a parameter. I don’t typically advocate defining public
properties except by way of getter and setter methods. So all the examples I’ll
show you work with getter and setter methods. But you could just as well place
[Inspectable] on the line before you define
any public property within a class and it would work in the same way.

The following example shows how the [Inspectable]
tag can be placed just before a getter or setter method to make it a parameter.

[Inspectable]
public function set value(nValue:Number):Void {
_nValue = nValue;

}

If you have a pair of methods – one getter and one setter
– for the same property, you only need to place the [Inspectable]
metatag prior to one of them.

The [Inspectable] metatag
allows you to specify some additional information as a list of name-value pairs
within parentheses. You can consult the Flash Help panel for a complete list
of [Inspectable] information, but three of the
most important are:

* defaultValue – The default
value that Flash should use. This is the value that will get displayed in the
Component Inspector panel for the parameter when the component instance is first
dragged onto the stage.

[Inspectable(defaultValue=10)]

* type – The type of value.
This determines how the user can interact with the parameter value in Flash.
The default value is Default, which allows the user to enter a value by typing
it into the field in the Component Inspector. You can also specify things like
Boolean (which then gives the user a choice between true and false) and Color
(which prompts the user with a color selector.)

[Inspectable(type=Color)]

* enumeration – A list of
values from which the user can select. This is used when the type is Array.
The enumeration options should be a quote-enclosed comma-delimited list.

[Inspectable(type=Array, enumeration="a,b,c,d,e,f,g")]

When you update the parameters for a class by adding, removing,
or modifying [Inspectable] metatags, you must
open the Component Definition dialog box and click OK again for Flash to update
with the new information.

Handling Resizing

One thing that you’ll almost certainly need to do is set
up your component to correctly handle resizing. By default, when you resize
a component instance – either at authoring time with the Property inspector
or at runtime with the _width and _height properties
– Flash will simply scale the component. However, in most cases that will not
be what you want. For example, later in the article you’ll get a chance to create
a sample scroll bar component. A scroll bar consists of several parts, including
the up and down scroll arrow buttons, the scroll track, and the scroll thumb
bar. If you adjust the size of the scroll bar you don’t want the up and down
scroll arrow buttons to scale. They should remain the same size while the scroll
track should scale to fill the rest of the area between. Figure 1 shows a comparison
between the two scenarios.

Figure 1: The scroll bars are both resized to 150 pixels in
height. However, the one on the left is resize correctly such that the up and
down scroll arrow buttons are not distorted. The scroll bar on the right was
simply scaled uniformly.

Therefore, you will likely want to handle resizing intelligently.
There are two types of resizing that can occur – authoring time resizing caused
by a change in the values in the Property inspector, and runtime resizing caused
by adjusting the various properties of the component instance. Each of these
should be handled differently, so let’s look at them separately.

First, let’s look at authoring time resizing. Authoring time
resizing really only affects live preview appearance, and it doesn’t actually
have any effect on the runtime appearance. But live preview appearance can be
just as important. If you are creating authoring time instance of your component,
you’ll want to be able to see what the changes you are making will have on the
component. When you set the values for width and height in the Property inspector,
it really just scales the instance of the movie clip or component. In order
to be able to detect and handle resizing you should implement a setSize()
method in your component class. Unlike many of the other methods I’ve mentioned
previously in this article (init(), draw(),
etc.) the setSize() method name is something
that is hardcoded into Flash. When the authoring time dimensions of a component
instance change Flash will automatically look for and call the setSize()
method of the associated component class, and it passes the method two parameters
– the first specifying the newly assigned width and the second specifying the
newly assigned height. Within your setSize()
method you will probably want to do at least two things:

* Set the scaling of the instance back to 100.

* Call the arrange() and
possibly the draw() methods.

Typically I also create __width
and __height properties (each of those is with
two underscores so as to differentiate them from the built-in _width
and _height properties) within component classes.
I use those properties to hold the actual dimensions for the component instance.
Since you are going to scale the instance back to 100 percent, you’ll lose the
new width and height values that have been assigned to _width
and _height (single underscore). So I recommend you assign the value of the
first parameter to __width and the second parameter
value to __height (both with double underscores)
and then use __width and __height
in your calculations within your other methods such as draw()
and arrange(). This will likely be clearer
to you when you get a chance to work through the sample component later in the
article.

So you can get an idea of what your setSize()
method implementation might look like, consider the following:

private function setSize(nW:Number, nH:Number):Void {
_xscale = 100;
_yscale = 100;
if(nW != null) {
__width = nW;
}
if(nH != null) {
__height = nH;
}
arrange();
}

You should also make sure to set the scaling and the __width
and __height properties in your class’s init()
method. In that case you’ll be assigning the value of _width
to __width, and the value of _height
to __height. So make sure you do those assignments
before you set the scaling back to 100. For example:

private function init():Void {
__width = _width;
__height = _height;
_xscale = 100;
_yscale = 100;
mcBoundingBox._visible = false;
mcBoundingBox._width = 0;
mcBoundingBox._height = 0;
}

The next thing you need to be able to handle is runtime resizing.
If you’ve used any of the v2 components then you’ll notice that they have width
and height properties for resizing them rather than using the _width
and _height properties built into the MovieClip
superclass. If you try resizing a v2 component instance using the _width and
_height properties then you’ll see that they
scale rather than resizing as you’d expect. The width and height properties
are defined within the v2 framework as getter and setter methods so that the
components can handle resizing intelligently. I recommend you do the same with
your own components.

Typically the way that I recommend implementing the width
and height getter and setter methods is as follows:

* In the setter methods simply call setSize().

* In the getter methods return the value of __width
or __height.

For example, the following shows a height getter and setter
method pair.

  public function set height(nHeight:Number):Void {
setSize(null, nHeight);
}

public function get height():Number {
return __height;
}

The only other consideration with resizing is, as I have
already mentioned, that you use __width and
__height (double underscores) in your other
methods to calculate how you draw and arrange things. If you use the built-in
_width and _height
(single underscores) then it will just consistently draw and arrange things
to the default size.

Dispatching Events

Another thing you’ll want to be able to have your components
do is dispatch events. If you haven’t yet familiarized yourself with the listener
model for the v2 components, you might want to do that before reading further.
Just to refresh your memory, however, I’ll summarize the details.

You can add an object to a v2 component as a listener using
the addEventListener() method. When an event
occurs within the component, it automatically notifies all listeners that are
registered to receive that type of event. Flash then calls the method of the
listener object that has the same name as the event. For example, if you have
a Button instance then you can register a listener
object to handle click events. The following code demonstrates this:

var oListener:Object = new Object();

oListener.click = function(oEvent:Object):Void {
trace(oEvent.target + " was clicked");

};

cbSubmitButton.addEventListener("click", oListener);

The simplest way to enable your components to dispatch events
to listeners is to use the same mechanism as the v2 components use. In the mx.events
package (a package of classes that ships with Flash) you’ll find an EventDispatcher
class. You can call the class’s static method, initialize(),
from within your component class to set up the class to be able to register
listeners and dispatch events. The initialize() method
requires a parameter that references the object for which you want to enable
the event dispatching. In the case of your component the value should be this.
I typically recommend you call the mx.events.EventDispatcher.initialize()
method from within your class’s init() method.
For example (line shown in bold):

private function init():Void {
__width = _width;
__height = _height;
_xscale = 100;
_yscale = 100;
mx.events.EventDispatcher.initialize(this);
mcBoundingBox._visible = false;
mcBoundingBox._width = 0;
mcBoundingBox._height = 0;
}

Then, to dispatch an event, you can use the dispatchEvent()
method that your class will automatically "inherit" (in quotes
because it is not really inheritance) from EventDispatcher.
And the class will also "inherit" the addEventListener()
and removeEventListener() methods so
that you can register listener object with instances of the class. The only
thing is that you must make sure to declare dispatchEvent(),
addEventListener(), and removeEventListener()
as a class member along with the other class members as follows:

private var dispatchEvent:Function;
public var addEventListener:Function;
public var removeEventListener:Function;

That simply lets the class know that the method exists. If
you don’t add that line of code to the class then Flash will give you an error
when you try to compile.

The dispatchEvent() method
requires one parameter – an object with at least two parameters: type
and target. The type
property should be a string specifying the name of the event. The target
property should be a reference to the object dispatching the event. In most
cases that will be this. The following is an
example of how to dispatch an event.

dispatchEvent({type: "click", target: this});

As with much of what has been discussed so far, don’t worry
too much if you don’t feel you quite "get" it yet. It will likely
be much clearer to you when you get a chance to work through an example.

Exporting a compiled clip

Once you’ve created your component symbol and class, you
will likely want to export it as a compiled clip into an .swc file. An .swc
is a zip format that contains all the necessary elements of your component –
including live preview. After you’ve exported the .swc file you can place it
in the appropriate directory so that it will show up in your Components panel
so that you can utilize the component in your other Flash applications. You
can also package it as an .mxp so that you can distribute it to other developers.

Exporting a .swc file is really extraordinarily simple. All
you need to do is select the component movie clip symbol from the library, right-click/command-click,
and choose Export SWC from the menu. Flash will prompt you as to where you want
to save it. Choose a location, and then save the .swc file.

In order to add the component to your Components panel select
the .swc file and copy it to the appropriate directory on your computer. On
Windows XP that directory is likely something like: C:\Documents and Settings\[User
Name]\Local Settings\Application Data\Macromedia\Flash MX 2004\en\Configuration\Components.
I don’t use a Macintosh, so I don’t know what the directory on Mac is, but you
can probably figure it out based on the Windows XP information. Once you’ve
added a copy of the .swc file to that directory you can reload the Components
panel. To do so, open the Components panel, select the panel menu, and choose
Reload. Once it reloads you should see the component added to the list.

Note: You should save the .swc file into a subdirectory within
the Components directory.

In order to package the component in an .mxp file you need
to make sure you have the latest Extension Manager. You then need to create
an .mxi file. The .mxi format is just XML. The following listing is an example
you can adapt.

<macromedia-extension
name="com.person13.SampleScrollBar"
version="1.0"
type="flashcomponent">

<!-- Describe the author -->

<author name="Joey Lott" />

<!-- List the required/compatible products -->

<products>
<product name="Flash" version="7" primary="true" />
</products>

<!-- Describe the extension -->

<description>
<![CDATA[
Window component.
]]>
</description>

<!-- Describe where the extension shows in the UI of the product -->

<ui-access>
<![CDATA[
Components panel.
]]>
</ui-access>

<!-- Describe the files that comprise the extension. Make sure that you save
The .mxi in the same directory as the .swc file(s)
-->

<files>
<file name="SampleScrollBar.swc" destination="$flash/Components/" />
</files>

</macromedia-extension>

You can then open the Extension Manager to package the component.
Choose File > Package Extension. From the dialog box choose the .mxi file
and click OK. Then select the name and location to which you will save the .mxp
file. Export the .mxp. You can then distribute the .mxp. Anyone who has the
Extension Manager will be able to install your component with a few clicks.

Making a Sample Component

Thus far you’ve read a lot of theory. How about putting it
into practice? In this exercise you’ll create a sample scroll bar component.
The scroll bar component will be fairly simple so that the principals involved
in creating a component are highlighted. You can feel free to later modify the
code to provide more functionality to the scroll bar.

1. Download the SampleScrollBar.zip
file
.

2. Extract the files from the .zip. It should create the
following:

/StarterFiles
– SampleScrollBar_starter.fla
– com/person13/SampleScrollBar.as

/CompletedFiles
– SampleScrollBar.fla
– com.person13/SampleScrollBar.as

The StarterFiles directory contains the files you should
use to get started. The .fla is there because it contains the artwork to get
started. The CompletedFiles directory is provided so you can see the working
example.

3. Open StarterFiles/SampleScrollBar_starter.fla, and open
the document’s library.

4. In the library you’ll find several movie clip symbols.
ScrollUp, ScrollDown, ScrollTrack, and ScrollThumb have already been set for
export and given linkage identifiers. ScrollBar has not been set for export
yet. So open the symbol’s linkage settings, and check the Export for ActionScript
option. Give it a linkage identifier of ScrollBar. And set the AS 2.0 Class
field to com.person13.SampleScrollBar.

5. Open the ScrollBar symbol in editing mode. You’ll see
that two layers and several keyframes have already been created for you. Add
a stop() action to the keyframe in the Actions
layer.

6. Drag an instance of BoundingBox onto the stage on the
first frame of the Assets layer. Name the instance mcBoundingBox,
resize it to 15 pixels by 100 pixels, and set the x and y coordinates to 0 and
0.

7. On the second frame of the Assets layer add instances
of ScrollUp, ScrollDown, ScrollTrack, and ScrollThumb.

8. Save the .fla file, and next open the com/person13/SampleScrollBar.as
file.

9. SampleScrollBar.as has been left intentionally blank.
Add the following code to the class file:

class com.person13.SampleScrollBar extends MovieClip {


// The BoundingBox instance created at authoring time within the symbol.
private var mcBoundingBox:MovieClip;

// The instances of the other movie clip symbol assets that you will create programmatically.
private var _mcScrollUp:MovieClip;
private var _mcScrollTrack:MovieClip;
private var _mcScrollDown:MovieClip;
private var _mcScrollThumb:MovieClip;

// The interval identifier used with setInterval() and clearInterval()
private var _nInterval:Number;

// The pixel offset based on where you click on the scroll thumb bar.
private var _nOffset:Number;

// The __width and __height properties we use to store the dimensions of the instance.
private var __width:Number;
private var __height:Number;

// The previous scroll value. This is used to make sure that the component only dispatches events
// when the value has actually changed.
private var _nPrevScroll:Number;

// The minimum and maximum scroll values. Default to 0 and 100.
private var _nMax:Number = 100;
private var _nMin:Number = 0;

// The placement of the scroll thumb bar.
private var _nScrollPosition:Number = 0;

// The dispatchEvent() method that gets added by EventDispatcher.
private var dispatchEvent:Function;


// The getter and setter methods for min and max.
[Inspectable(defaultValue=0)]
public function set min(nMin:Number):Void {
_nMin = nMin;
}

public function get min():Number {
return _nMin;
}

[Inspectable(defaultValue=100)]
public function set max(nMax:Number):Void {
_nMax = nMax;
}

public function get max():Number {
return _nMax;
}

// The getter and setter methods for scrollPosition. When the scrollPosition is set, assign the value
// to the private property, and then call arrange(). When the value is gotten, calculate the value
// based on the thumb bar's _y coordinate and the min and max values.
[Inspectable(defaultValue=0,type=Number)]
public function set scrollPosition(nScrollPosition:Number):Void {
_nScrollPosition = nScrollPosition;
arrange();
}

public function get scrollPosition():Number {
var nVal:Number = (_mcScrollThumb._y - _mcScrollTrack._y) / (_mcScrollTrack._height - _mcScrollThumb._height) * (_nMax - _nMin) + _nMin;
return Math.round(nVal);
}

// The getter and setter methods for height. No need to define width because this simple scroll bar
// can only be vertical.
public function set height(nHeight:Number):Void {
setSize(null, nHeight);
}

public function get height():Number {
return __height;
}

// The setSize() method is automatically called when the authoring time instance is resized.
public function setSize(nW:Number, nH:Number):Void {
_xscale = 100;
_yscale = 100;
__width = nW;
__height = nH;
arrange();
}

// The constructor.
function SampleScrollBar() {
init();
createChildren();
arrange();
}

// Basic init() method as outlined in article.
private function init():Void {
__width = _width;
__height = _height;
_xscale = 100;
_yscale = 100;
mx.events.EventDispatcher.initialize(this);
mcBoundingBox._visible = false;
mcBoundingBox._width = 0;
mcBoundingBox._height = 0;
}

// The createChildren() method istantiates the up and down scroll arrow buttons, the scroll track, and the
// scroll thumb bar. Although in a completely abstracted example we might define classes for
// each of those elements, in this case we just assign the onPress(), onRelease(), and onReleaseOutside()
// methods to each of them within the createChildren() method.
private function createChildren():Void {
attachMovie("ScrollUp", "_mcScrollUp", getNextHighestDepth());
attachMovie("ScrollTrack", "_mcScrollTrack", getNextHighestDepth());
attachMovie("ScrollDown", "_mcScrollDown", getNextHighestDepth());
attachMovie("ScrollThumb", "_mcScrollThumb", getNextHighestDepth());
_mcScrollThumb.onPress = function():Void {
this._parent._nOffset = this._ymouse;
this._parent._nInterval = setInterval(this._parent, "scroll", 10);
};
_mcScrollThumb.onRelease = function():Void {
clearInterval(this._parent._nInterval);
};
_mcScrollThumb.onReleaseOutside = _mcScrollThumb.onRelease;
_mcScrollUp.onPress = function():Void {
this._parent._nInterval = setInterval(this._parent, "nudge", 10, -1);
};
_mcScrollUp.onRelease = function():Void {
clearInterval(this._parent._nInterval);
};
_mcScrollUp.onReleaseOutside = _mcScrollUp.onRelease;
_mcScrollDown.onPress = function():Void {
this._parent._nInterval = setInterval(this._parent, "nudge", 10, 1);
};
_mcScrollDown.onRelease = function():Void {
clearInterval(this._parent._nInterval);
};
_mcScrollDown.onReleaseOutside = _mcScrollDown.onRelease;
}

// Arrange all the elements. This method gets called when the instance is first created as well
// as every time it is resized.
private function arrange():Void {

// Set the height of the scroll track to be the height of the entire instance (given by __height)
// minus the default heights of the up and down arrow buttons.
_mcScrollTrack._height = __height - _mcScrollUp._height - _mcScrollDown._height;

// Place the scroll track just below the up arrow button.
_mcScrollTrack._y = _mcScrollUp._height;

// Place the down arrow button just below the scroll track.
_mcScrollDown._y = _mcScrollTrack._y + _mcScrollTrack._height;

// Place the scroll thumb bar one pixel to the right so it appears within the scroll track.
_mcScrollThumb._x = 1;

// Determine the vertical placement of the scroll thumb bar based on _nScrollPosition.
var nY:Number = ((_nScrollPosition - _nMin)/(_nMax - _nMin)) * (_mcScrollDown._y - _mcScrollThumb._height);
if(nY > _mcScrollDown._y - _mcScrollThumb._height) {
nY = _mcScrollDown._y - _mcScrollThumb._height;
}
else if(nY < _mcScrollTrack._y) {
nY = _mcScrollTrack._y;
}
_mcScrollThumb._y = nY;
}

// This method is called at an interval when the thumb bar is dragged by the user.
private function scroll():Void {

// Determine the vertical placement of the thumb bar based on the value of _ymouse minus
// the offset. Also, calculate the maximum and minimum values in the range. If the mouse is
// outside those values then use the corresponding maximum or minimum value so that the thumb
// bar does not leave the scroll track.
var nY:Number = _ymouse - _nOffset;
var nUpper:Number = _mcScrollTrack._y;
var nLower:Number = _mcScrollDown._y - _mcScrollThumb._height;
if(nY > nUpper && nY < nLower) {
_mcScrollThumb._y = _ymouse - _nOffset;
}
else if(nY < nUpper) {
_mcScrollThumb._y = nUpper;
}
else if(nY > nLower) {
_mcScrollThumb._y = nLower;
}

// Dispatch an event by calling the custom dispatchScrollEvent() method (defined later.)
dispatchScrollEvent();
updateAfterEvent();
}

// This method is called whenever the up or down arrow buttons are pressed. The functionality
// is very similar to the scroll() method. See the comments in the scroll() method for details.
private function nudge(nIncrement:Number):Void {
var nUpper:Number = _mcScrollTrack._y;
var nLower:Number = _mcScrollDown._y - _mcScrollThumb._height;
_mcScrollThumb._y += nIncrement;
if(_mcScrollThumb._y < nUpper) {
_mcScrollThumb._y = nUpper;
}
else if(_mcScrollThumb._y > nLower) {
_mcScrollThumb._y = nLower;
}
dispatchScrollEvent();
updateAfterEvent()
}

// If the current value differs from the previous value, dispatch a scroll event.
private function dispatchScrollEvent():Void {
if(_mcScrollThumb._y != _nPrevScroll) {
dispatchEvent({type: "scroll", target: this});
_nPrevScroll = _mcScrollThumb._y;
}
}

}

10. Save the .as file and return to the .fla.

11. Select the ScrollBar symbol from the library and open
the Component Definition dialog box.

12. Enter the value of com.person13.SampleScrollBar to the
AS 2.0 Class field and click OK.

13. Select the ScrollBar symbol and choose the Export SWC
option. Export the file to the Components/UI Components directory as discussed
earlier in this article.

14. Open a new .fla document.

15. Open the Components panel and reload it. The ScrollBar
component should appear in the panel.

16. Drag an instance of ScrollBar from the Components panel
to the stage on the main timeline. Name the instance csbScroller.

14. Open the Component Inspector panel. You should see the
properties max, min, and scrollPosition. Set the values to 200, -200, and 100.

15. Using the Property inspector resize the component instance
to 200 pixels in height.

16. Add a new layer to the main timeline and add the following
actions to the keyframe in that layer:

var oListener:Object = new Object();

oListener.scroll = function(oEvent:Object):Void {
trace(oEvent.target.scrollPosition);

};

csbScroller.addEventListener("scroll", oListener);

17. Test the movie. You should see the scrollPosition value write to the Output
panel as you scroll the scroll bar.

Conclusion

Components are a rather powerful and sometimes confusing
subject. Hopefully this article has helped you get started writing components.
It is not intended to be an exhaustive reference on the subject, however. Please
do email me with your feedback and with an corrections you might have. And,
as usual, thanks for reading.

Leave a Reply