Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning ActionScript 2.0 2006

.pdf
Скачиваний:
104
Добавлен:
17.08.2013
Размер:
12.47 Mб
Скачать

Chapter 27

How It Works

The thumbnail class shown here does not yet load an image, however it does demonstrate the use of the constructor and private properties.

First, get an idea of what the movie clip structure is going to be. Figure 27-1 shows the movie clip structure as displayed in the debugger movie clip browser pane. The base movie clip for the thumbnail is _level0.myThumbnail. The _level0 reference corresponds to what was passed to the parent timeline parameter, and the myThumbnail reference corresponds to the thumbnail name parameter. Within that movie clip are two more movie clips. One is imageHolder, which will be used to load the actual thumbnail image. The other is borderHolder, which will be used for drawing a border that overlays the image.

Figure 27-1

Within the code, the private properties are the first to be declared. The first two, pParentTimeline and pThumbnailName, are used to hold the data passed into the two constructor parameters. The next three are handles to the three different movie clips created and are there for quick convenient access to those movie clips. The second-to-last property, pClickHandler, is for holding a handle to a function that will be called when the button is clicked, and will be used a bit later. The last one is for a loader class that will be used for loading the thumbnail image:

private var pParentTimeline:MovieClip; private var pThumbnailName:String; private var pThumbnailHolder:MovieClip; private var pImageHolder:MovieClip; private var pBorderHolder:MovieClip; private var pClickHandler:Function;

private var pMovieClipLoader:MovieClipLoader;

The startup code is placed within the constructor. First the two input parameters are passed to two private properties:

pParentTimeline = parentTimeline; pThumbnailName = thumbnailName;

Next, the base movie clip is created and a handle is assigned to one of the properties:

pThumbnailHolder = pParentTimeline.createEmptyMovieClip(pThumbnailName, ; pParentTimeline.getNextHighestDepth());

The movie clip that is to hold the actual image is created within the base movie clip, and a handle to the movie clip is assigned to one of the properties for quick access in other parts of the code:

668

Creating Custom Classes

pImageHolder = pThumbnailHolder.createEmptyMovieClip(“imageHolder”, ; pThumbnailHolder.getNextHighestDepth());

The movie clip that is to hold the thumbnail border is created within the base movie clip, and a handle to the movie clip is assigned to one of the properties:

pBorderHolder = pThumbnailHolder.createEmptyMovieClip(“borderHolder”, ; pThumbnailHolder.getNextHighestDepth());

An instance of the MovieClipLoader class is created and a handle is assigned to a property:

pMovieClipLoader = new MovieClipLoader(); pMovieClipLoader.addListener(this);

At this point, you have a class shell that works, but does not yet do anything that is visible on the screen. Before you can make this class do anything useful, you need to add at least one method. You see methods in action next.

Defining Methods

Class methods are defined in almost exactly the same way that functions are defined. They can accept any number of input parameters, and they define the data type of the return value, if any. The only difference in how a method is declared versus how a function is declared is that a method must exist within the class block, and it may be preceded with either the keyword public or the keyword private to indicate whether or not the method can be accessed outside of the class. The following snippet shows a sample class method declaration:

public function setLabel(newLabelText:String):Boolean

{

// ...

return true;

}

A method can be called from any other method within the same class by simply invoking it in the same way that a function is invoked. Here, drawThumbnail() calls setLabel(), a method of the same class:

public function drawThumbnail():Void

{

var labelFits:Boolean = setLabel(“Galiano Sunset”);

}

Any method can call any other method within the same class, regardless of whether the method being called is public or private. Setting a method to be private just prevents the method from being accessed from any code outside of the class.

If a method declaration does not include the keyword public or private, it is assumed to be public.

With ActionScript 1.0, methods are called within a class by calling this.myMethod(). The this keyword tells the compiler that the method is referring to the parent object, namely the class instance. This still works in ActionScript 2.0 but is no longer necessary and is generally omitted to keep the code visually clean.

669

Chapter 27

You have already seen many examples of public methods being called from the Flash built-in classes, and calling a public method from your custom classes is no different:

var successValue:Boolean = myInstance.myMethod(“foo”, 2);

Take a look at methods in use by adding them to your thumbnail class.

Try It Out

Adding Methods to a Photo Thumbnail Class

In this example, you add some methods to the thumbnail class that you started in the previous Try It Out exercise.

1.Open the completed tryItOut_thumbnailButton.fla file from the first Try It Out exercise or open up the source Flash project file from the book’s source files at <source file directory>/ Chapter 27/tryItOut_thumbnail_v1/tryItOut_thumbnailButton.fla.

2.Open the completed ThumbnailButton.as file from the first Try It Out exercise, or open up the source Flash project file from the book’s source files at <source file directory>/Chapter 27/ tryItOut_thumbnail_v1/ThumbnailButton.as.

3.Add the following two methods just after the constructor, but before the closing brace for the class block:

public function load(imageURL:String):Void

{

pMovieClipLoader.loadClip(imageURL, pImageHolder);

}

private function onLoadInit(targetMovieClip:MovieClip):Void

{

trace(“Load complete”);

}

4.Save the file (File Save).

5.Using your favorite image editor, resize and crop any photo into a small image around 50 pixels by 50 pixels. Save it as a JPG file into the same directory containing the FLA and AS files. You can instead obtain a sample image in the book source files at <source file directory>/Chapter 27/ tryItOut_thumbnail_v2/galianoSunset_over.jpg.

6.Return to the FLA file, click the first frame in the timeline, open the Actions panel (Window Development Panels Actions), and update the ActionScript code to look like the following code. If you have named your image file something different, change the parameter in the load() method to refer to your image:

var myThumbnail:ThumbnailButton = new ThumbnailButton(this, “myThumbnail”);

myThumbnail.load(“galianoSunset_over.jpg”);

7.Choose Control Test Movie to try it out.

8.Return to the AS file. Update the onLoadInit() method with the following code:

private function onLoadInit(targetMovieClip:MovieClip):Void

{

var parent:ThumbnailButton = this;

pThumbnailHolder.onPress = function()

670

Creating Custom Classes

{

trace(“button pressed”);

}

pThumbnailHolder.onRelease = function()

{

trace(“button released”);

}

pThumbnailHolder.onReleaseOutside = function()

{

trace(“button released outside”);

}

pThumbnailHolder.onRollOver = function()

{

trace(“button rolled over”);

}

pThumbnailHolder.onRollOut = function()

{

trace(“button rolled out”);

}

}

9.Return to the FLA file and choose Control Test Movie to try it out. Try mousing over and clicking the thumbnail button.

10.Return to the AS file. Update onLoadInit() with the following code:

private function onLoadInit(targetMovieClip:MovieClip):Void

{

var parent:ThumbnailButton = this;

drawThumbnail(“up”);

pThumbnailHolder.onPress = function()

{

parent.drawThumbnail(“down”);

}

pThumbnailHolder.onRelease = function()

{

if (parent.pClickHandler != null)

{

parent.pClickHandler(parent);

}

parent.drawThumbnail(“over”);

}

pThumbnailHolder.onReleaseOutside = function()

{

parent.drawThumbnail(“up”);

}

pThumbnailHolder.onRollOver = function()

671

Chapter 27

{

parent.drawThumbnail(“over”);

}

pThumbnailHolder.onRollOut = function()

{

parent.drawThumbnail(“up”);

}

}

11.Add the following method just after the onLoadInit() method, but before the closing brace for the class block:

private function drawThumbnail(thumbnailState:String):Void

{

var dropShadowFilter:DropShadowFilter; var appliedFilterArray = new Array(); pBorderHolder.clear(); switch(thumbnailState)

{

case “up”:

dropShadowFilter = new DropShadowFilter(3, 45, 0x666666, ; 0.8, 5, 5, 1, 3, false, false, false);

pImageHolder._x = pBorderHolder._x = 0; pImageHolder._y = pBorderHolder._y = 0; pBorderHolder.lineStyle(1, 0x666666); break;

case “over”:

dropShadowFilter = new DropShadowFilter(3, 45, 0x000000, ; 1, 5, 5, 1, 5, false, false, false);

pImageHolder._x = pBorderHolder._x = 0; pImageHolder._y = pBorderHolder._y = 0; pBorderHolder.lineStyle(1, 0x000000); break;

case “down”:

dropShadowFilter = new DropShadowFilter(1, 45, 0x000000, ; 1, 3, 3, 1, 5, false, false, false);

pImageHolder._x = pBorderHolder._x = 2; pImageHolder._y = pBorderHolder._y = 2; pBorderHolder.lineStyle(1, 0x990000); break;

}

pBorderHolder.moveTo(0, 0); pBorderHolder.lineTo(pImageHolder._width-1, 0); pBorderHolder.lineTo(pImageHolder._width-1, pImageHolder._height-1); pBorderHolder.lineTo(0, pImageHolder._height-1); pBorderHolder.lineTo(0, 0);

appliedFilterArray.push(dropShadowFilter); pImageHolder.filters = appliedFilterArray;

}

12.Return to the FLA file and choose Control Test Movie to try it out.

672

Creating Custom Classes

How It Works

This example adds one public and two private methods to the class definition. The public method is a load method that the person using this class would call to load up the image. The onLoadInit() and drawThumbnail() methods are used for internal implementation and are made private so that anyone using the class cannot directly call these methods.

First, start with the load() method. This method accepts a path to an image to load. After it’s loaded, it invokes the MovieClipLoader class to load it into the image holder movie clip:

public function load(imageURL:String):Void

{

pMovieClipLoader.loadClip(imageURL, pImageHolder);

}

You may notice that the onLoadInit() method is not actually called from anywhere in the code, yet the trace that shows up in the output indicates that it is called. The MovieClipLoader class is the one calling the method. When the MovieClipLoader class was instantiated at the end of the ThumbnailButton constructor, it was passed the this parameter, which set up the ThumbnailButton class itself as an event listener. The MovieClipLoader class can generate several events, one of which is the onLoadInit event. The loader automatically checks whether the onLoadInit() method exists in the ThumbnailButton class, and it calls the method automatically when the image has finished loading.

Take a closer look at the contents of the onLoadInit() method. The first few lines warrant some explanation:

var parent:ThumbnailButton = this;

drawThumbnail(“up”);

pThumbnailHolder.onPress = function()

{

parent.drawThumbnail(“down”);

}

The first line creates a local variable and assigns the handle to this copy of the class. This will be important in a moment. The second line calls the drawThumbnail() method. The last four lines add an onPress event handler to the thumbnail holder movie clip.

The part that needs some explanation here is how drawThumbnail() is called from within this event handler. The problem is that just calling drawThumbnail() within the event handler will not work. When a method is called, it is called in the context of the parent class. In this case, the parent class is pThumbnailHolder, which is a movie clip:

pThumbnailHolder.onPress = function()

{

drawThumbnail(“down”);

}

The effect is the same as if the method were called directly:

pThumbnailHolder.drawThumbnail(“down”);

673

Chapter 27

This is not the behavior that you want, and the preceding method will not run because it does not exist in the context of the movie clip. To call a method from the ThumbnailHolder class instead, the parent variable is created. Local variables are visible within anonymous function blocks, so parent is visible to code within the event handler block. The parent variable points to the ThumbnailHolder class instance, and parent.drawThumbnail(“down”); refers to the correct drawThumbnail() method.

The remaining code within this method defines how to make the thumbnail button respond to the other mouse events. The only thing different is the onRelease event handler. When the mouse is released over the button, a function is to be called. You define this function in the next exercise.

pThumbnailHolder.onRelease = function()

{

if (parent.pClickHandler != null)

{

parent.pClickHandler(parent);

}

parent.drawThumbnail(“over”);

}

The drawThumbnail() method is a private method that is responsible for adding the drop shadow and the outline to the thumbnail image. The first two lines within the drawThumbnail() method set up the variables to hold the drop shadow filter. The clear() method removes the border from the last time drawThumbnail() was called:

var dropShadowFilter:DropShadowFilter; var appliedFilterArray = new Array(); pBorderHolder.clear();

Within the switch..case statement, the drop shadow is created with settings that correspond with the button state. If the button is in the down state, the drop shadow is placed closer to the image, and the image and border is moved two pixels down and to the right. This gives the appearance of the button being physically pressed. The lineStyle() method is called to set the line color, resulting in the different border colors for the different button states:

switch(thumbnailState)

{

case “up”:

dropShadowFilter = new DropShadowFilter(3, 45, 0x666666, ; 0.8, 5, 5, 1, 3, false, false, false);

pImageHolder._x = pBorderHolder._x = 0; pImageHolder._y = pBorderHolder._y = 0; pBorderHolder.lineStyle(1, 0x666666); break;

case “over”:

dropShadowFilter = new DropShadowFilter(3, 45, 0x000000, ; 1, 5, 5, 1, 5, false, false, false);

pImageHolder._x = pBorderHolder._x = 0; pImageHolder._y = pBorderHolder._y = 0; pBorderHolder.lineStyle(1, 0x000000); break;

case “down”:

dropShadowFilter = new DropShadowFilter(1, 45, 0x000000, ;

674

Creating Custom Classes

1, 3, 3, 1, 5, false, false, false); pImageHolder._x = pBorderHolder._x = 2; pImageHolder._y = pBorderHolder._y = 2; pBorderHolder.lineStyle(1, 0x990000); break;

}

Finally, the border is drawn, and the drop shadow filter is applied to the image:

pBorderHolder.moveTo(0, 0); pBorderHolder.lineTo(pImageHolder._width-1, 0); pBorderHolder.lineTo(pImageHolder._width-1, pImageHolder._height-1); pBorderHolder.lineTo(0, pImageHolder._height-1); pBorderHolder.lineTo(0, 0);

appliedFilterArray.push(dropShadowFilter); pImageHolder.filters = appliedFilterArray;

There is still a bit of functionality remaining to be implemented. You first take a look at some issues surrounding defining properties, and then you finish off this class.

Defining Properties

Although you have already seen a number of examples of properties in use in the previous Try It Out exercises, they still warrant further study. The first Try It Out exercise showed a public property in use, where you could directly manipulate the property. Say that you have the following class based on that exercise:

Class HelloWorldClass

{

public var messageText:String;

public function HelloWorldClass()

{

// ...

}

}

The messageText property is public, and as such can be viewed and modified directly:

var myInstance:HelloWorldClass = new HelloWorldClass(); myInstance.messageText = “foo”; trace(myInstance.messageText);

Unfortunately, a few problems exist with exposing the property directly like this:

The property is always writable; you have no ability to make the property read-only.

There is no elegant way to call additional code if the property value changes.

If you were to change the implementation of the class, any public properties that change as a result would result in code changes for anyone who has implemented your class.

675

Chapter 27

Fortunately, there is a clean way to address these issues in the form of getters and setters.

Working with Getters and Setters

Getters and setters are special methods that control access to your public properties. A getter is a method that controls what happens when a developer attempts to read from a public property, and a setter is a method that controls what happens when a developer attempts to write to a public property. Take your small HelloWorldClass example and implement that with a getter and a setter instead:

Class HelloWorldClass

{

private var pMessageText:String;

public function HelloWorldClass()

{

// ...

}

public function get messageText():String

{

return pMessageText

}

public function set messageText(newText:String):Void

{

pMessageText = newText;

}

}

Note that the public property that was previously declared at the top of the class definition is now private and so the data behind the property is now hidden from anyone using the class. Also, note that the private property has now been renamed. It is a good idea to use a naming convention to make it clear in the code when a private property is being referenced. The authors use the convention of prepending a p to any private property. Other developers like to prepend an underscore character (_) to the variable name. Whatever you decide to use, just be consistent.

To retrieve the value of the property, simply call the property as you did previously:

trace(myInstance.messageText);

To set the value of the property, again you just set the property as you did previously:

myInstance.messageText = “foo”;

When the value of the property is being retrieved, Flash automatically calls the get version of the messageText() method. When the value of the property is being set, Flash automatically calls the set version of the messageText() method.

Implications of Getters and Setters

You can control whether or not properties are writable. If you define a getter but not a setter, it means that the property is readable, but not writable. Many reasons exist for why you would want to make a property read-only. Some properties may provide status, such as whether or not data has finished loading.

676

Creating Custom Classes

Other properties may describe data, such as the number of elements in an array. In both of these cases, it would not make sense to make the properties writable.

You also can easily call additional code when a property is set or retrieved. Say you want to audit any changes to a particular property. You can easily call code to log the change anytime the variable changes. The following code shows how a trace() statement can be called every time the setter is called, something that is very useful for debugging:

public function set messageText(newText:String):Void

{

trace(“pMessageText - old: ‘“ + pMessageText + “‘ new: ‘“ + newText + “‘“); pMessageText = newText;

}

Getters and setters prevent the developer who uses your class from getting direct access to your class data. You control precisely what the developer can access, and you can make changes to your implementation without affecting others. Say you wanted to do something offbeat such as store your message as an array of characters rather than as a string. You could change your private property to be an array, and you could update the setter and the getter to split and join the data between the string and the array data types without the developer knowing or caring about your change.

Getters allow for the concept of calculated properties. Say you want to provide a property that provides the number of words in the saved message. Such a getter might look like this:

public function get numWords():Number

{

var messageArray:Array = pMessageText.split(“ “); return messageArray.length;

}

There is no private property keeping track of the number of words. Instead, the value is calculated based on another private property.

Try It Out

Adding Properties to a Photo Thumbnail Class

In this example, you add methods to a class to create a clickable button out of a dynamically loaded thumbnail image.

1.Open the completed tryItOut_thumbnailButton.fla file from the previous Try It Out exercise, or open up the source Flash project file from the book’s source files at <source file directory>/ Chapter 27/tryItOut_thumbnail_v2/tryItOut_thumbnailButton.fla.

2.Open the completed ThumbnailButton.as file from the previous Try It Out exercise, or open up the source Flash project file from the book’s source files at <source file directory>/Chapter 27/ tryItOut_thumbnail_v2/ThumbnailButton.as.

3.Add the following two private properties at the top of the class definition:

private var pParentTimeline:MovieClip;

...

private var pMovieClipLoader:MovieClipLoader; private var pDrawDropShadow:Boolean = true; private var pDrawBorder:Boolean = true;

677