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

Beginning ActionScript 2.0 2006

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

Chapter 27

4.Add the following two if statements at the end of the drawThumbnail() method:

if (pDrawBorder == true)

{

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);

}

if (pDrawDropShadow == true)

{

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

}

5.Add the following getters and setters just after the end of the drawThumbnail() method, but before the closing brace for the class block:

public function get onClick():Function

{

return pClickHandler;

}

public function set onClick(clickHandler:Function):Void

{

pClickHandler = clickHandler;

}

public function get x():Number

{

return pThumbnailHolder._x;

}

public function set x(xPosition:Number):Void

{

pThumbnailHolder._x = xPosition;

}

public function get y():Number

{

return pThumbnailHolder._y;

}

public function set y(yPosition:Number):Void

{

pThumbnailHolder._y = yPosition;

}

public function get drawBorder():Boolean

{

return pDrawBorder;

}

public function set drawBorder(drawBorderSetting:Boolean):Void

678

Creating Custom Classes

{

pDrawBorder = drawBorderSetting; drawThumbnail(“up”);

}

public function get drawDropShadow():Boolean

{

return pDrawDropShadow;

}

public function set drawDropShadow(drawDropShadowSetting:Boolean):Void

{

pDrawDropShadow = drawDropShadowSetting; drawThumbnail(“up”);

}

public function get thumbnailName():String

{

return pThumbnailName;

}

6.Using your favorite image editor, resize and crop three additional photos into three small images around 50 pixels by 50 pixels. Save them as JPG files into the same directory containing the FLA and AS files. You can instead obtain sample images in the book source files at <source file directory>/Chapter 27/tryItOut_thumbnail_v2/.

7.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 files something different, change the parameters in the load() methods to refer to your images:

var thumbnailArray:Array = new Array(); var thumbnailHandle:ThumbnailButton;

for (var i=0; i < 4; i++)

{

thumbnailHandle = new ThumbnailButton(this, “thumbnail” + i); thumbnailHandle.x = i * 70 + 20;

thumbnailHandle.y = 20; thumbnailHandle.drawDropShadow = true; thumbnailHandle.drawBorder = true;

thumbnailHandle.onClick = function(thumbnailHandle:ThumbnailButton)

{

trace(“Clicked on button: “ + thumbnailHandle.thumbnailName);

}

thumbnailArray.push(thumbnailHandle);

}

thumbnailArray[0].load(“aStudyInTexture_over.jpg”); thumbnailArray[1].load(“buntzenWinter_over.jpg”); thumbnailArray[2].load(“flowerInDetail_over.jpg”); thumbnailArray[3].load(“galianoSunset_over.jpg”);

8.Choose Control Test Movie to try it out.

679

Chapter 27

How It Works

The getters and setters that you add to the class perform a mixture of tasks. The first is the onClick property. This property allows the developer to specify what function should be called when the button is pressed and then released. Only the setter is really needed here, but it is generally considered proper to always provide a getter along with a setter. If you can set a property, you should be able to read from it, too:

public function get onClick():Function

{

return pClickHandler;

}

public function set onClick(clickHandler:Function):Void

{

pClickHandler = clickHandler;

}

The getters and setters for the thumbnail position x and y values refer to the x and y properties of the thumbnail holder movie clip:

public function get x():Number

{

return pThumbnailHolder._x;

}

public function set x(xPosition:Number):Void

{

pThumbnailHolder._x = xPosition;

}

public function get y():Number

{

return pThumbnailHolder._y;

}

public function set y(yPosition:Number):Void

{

pThumbnailHolder._y = yPosition;

}

The drawBorder property enables you to specify whether or not to draw the box around the image. When the setter is called, it changes the private property and it forces the thumbnail to redraw immediately:

public function get drawBorder():Boolean

{

return pDrawBorder;

}

public function set drawBorder(drawBorderSetting:Boolean):Void

{

pDrawBorder = drawBorderSetting; drawThumbnail(“up”);

}

680

Creating Custom Classes

The drawDropShadow property enables you to specify whether or not to draw the shadow behind the image. When the setter is called, it changes the private property and it forces the thumbnail to redraw immediately:

public function get drawDropShadow():Boolean

{

return pDrawDropShadow;

}

public function set drawDropShadow(drawDropShadowSetting:Boolean):Void

{

pDrawDropShadow = drawDropShadowSetting; drawThumbnail(“up”);

}

Finally, the thumbnailName property is read-only and enables the name of the thumbnail button to be retrieved when the onClick event handler is called:

public function get thumbnailName():String

{

return pThumbnailName;

}

Next, you see how you can create a new class to extend functionality of an existing class.

Adding Functionality to Existing Classes

One question that comes up frequently in various forums is how to add functionality to the built-in Flash classes. There are different ways of doing this, including subclassing, static classes, and composition.

In ActionScript 1.0, classes can be directly modified using the prototype property of the class. This technique is generally discouraged because it affects code globally and as such can interfere with code re-use.

Subclassing

One of the concepts brought up in Chapter 5 was that of subclassing and inheritance. Subclassing involves creating a class that uses another class as a template, and then adds or modifies functionality. The new class inherits all of the functionality from the parent class.

Although this sounds like it’s exactly what you want, subclassing is generally not a great way to extend built-in classes. Numerous examples on various online forums try to use subclassing to extend built-in classes, but in fact end up using another technique called composition. The following code shows how a new method can be added to the String class using subclassing:

class MyString extends String

{

public function MyString(inputString:String)

{

super(inputString);

}

public function replace(searchString:String, replaceString:String):Void

681

Chapter 27

{

var newVal:String = split(searchString).join(replaceString); super(newVal);

}

}

The extends keyword indicates that the new class MyString is a subclass of the String class. The constructor takes in a single string parameter, which is the same parameter that the parent String class normally accepts. The super() method passes that parameter to the parent class, namely the String class.

The replace() method performs a search-and-replace operation on the string that was initially passed in to the constructor. The split() method that is called is inherited from the String class and knows to work on the string that was initially passed to the constructor because the super() method passed the text to the String class’s constructor.

The following code makes use of this new class:

var myMessage:MyString = new MyString(“The quick brown fox”); myMessage.replace(“quick”, “quack”); myMessage.replace(“brown”, “red”); trace(myMessage.toUpperCase());

// Outputs: THE QUACK RED FOX

Note that the MyString class now supports the new replace() method, plus it automatically inherited toUpperCase() from the String class.

Although this looks nice, you will face problems if you try to assign new data to your new MyString instance, something that you would expect to be able to do. For instance, if you assign the output of the toUpperCase() method to the MyString instance, you will get a compile error:

myMessage = myMessage.toUpperCase();

**Error** Scene=Scene 1, layer=Layer 1, frame=1:Line 4: Type mismatch in assignment statement: found String where MyString is required.

myMessage = myMessage.toUpperCase();

The problem is that myMessage is of type MyString, and myMessage.toUpperCase() returns a value of type String. To make this work, you need to convert the String data type to MyString:

myMessage = new MyString(myMessage.toUpperCase());

This starts to get real cumbersome real fast. Although having a subclass inherit all of the parent class’s methods is nice, it is not very practical for built-in classes. A cleaner way of doing this is through a static class, which you look at next.

Static Library

Some built-in classes do not need to be instantiated for you to be able to call some of its methods. For instance, Math.random() returns a random number, Key.getCode() returns the character code of a key pressed on the keyboard, and System.setClipboard(“foo”) places the text “foo” into the system

682

Creating Custom Classes

clipboard. In none of these cases do you have to call new Math(), new Key(), or new System() before calling the method. These methods are called static methods. Creating a class populated with static methods is a very handy way of creating your own library of utilities.

The following code shows how to create a class with a static method:

class MyLibrary

{

private function MyLibrary()

{

// Constructor is empty

}

public static function myStaticFunction(inputString:String):String

{

return “Input value: “ + inputString;

}

}

To call the static method, just call it directly from the class itself:

trace(MyLibrary.myStaticFunction(“foo”);

// Outputs: Input value: foo

Two things are different about this class definition. First, the constructor is set to be private. This class is not actually going to hold data, so it does not need to be instantiated. The fact that the constructor is private enforces this usage, because it will cause a compiler error if you attempt to instantiate it. Second, there is a static keyword included with the method declaration. It is this keyword that tells the compiler that this method can be called without instantiating the class.

In the next Try It Out exercise, you see how to use static methods to create a library of string handling functions.

Try It Out

Creating a String Handling Library

In this example, you use static methods to create a class containing a library of string handling routines.

1.Create a new ActionScript document by selecting File New and choosing ActionScript File from the New Document panel.

2.Select File Save As and choose an appropriate folder for the file. Give the file the name

StringLib.as and save it.

3.Enter the following code into the new ActionScript file:

class StringLib

{

private function StringLib()

{

// Nothing placed here

}

public static function replace(sourceString:String, ;

683

Chapter 27

searchString:String, replaceString:String):String

{

var newString:String = ; sourceString.split(searchString).join(replaceString);

return newString;

}

}

4.Save the file (File Save).

5.Create a new Macromedia Flash document by selecting File New and choosing Flash Document from the New Document panel.

6.Click the first frame in the timeline, open the Actions panel (Window Development Panels Actions), and enter in the following ActionScript code:

var myMessage:String = “ The quick brown fox. Jumped over the lazy dog. “; trace(“myMessage: ‘“ + myMessage + “‘“);

myMessage = StringLib.replace(myMessage, “brown”, “red”); trace(“myMessage.replace brown==>red: ‘“ + myMessage + “‘“);

7.Select File Save As, name the file tryItOut_stringLib.fla, and save it in the same directory as StringLib.as.

8.Choose Control Test Movie to try it out.

9.Switch to the AS file and add the following method after the replace() method but just before the final closing class bracket:

//Remove any leading or trailingspaces, tabs, or

//linefeeds from the string

public static function trimWhiteSpace(sourceString:String):String

{

var numChars:Number = sourceString.length; var firstCharPosition:Number = 0;

var lastCharPosition:Number = numChars-1; var whiteSpaceChars:String = “ \t\n\r”; var currentChar:String;

//Look for first non-whitespace character for (var i = 0; i < numChars; i++)

{

currentChar = sourceString.charAt(i);

if (whiteSpaceChars.indexOf(currentChar) == -1)

{

firstCharPosition = i; break;

}

}

//Look for last non-whitespace character

for (i = numChars - 1; i >= 0; i--)

{

currentChar = sourceString.charAt(i);

if (whiteSpaceChars.indexOf(currentChar) == -1)

{

lastCharPosition = i;

684

Creating Custom Classes

break;

}

}

return sourceString.slice(firstCharPosition, lastCharPosition+1);

}

10.Switch to the AS file and update the contents of the Actions panel:

var myMessage:String = “ The quick brown fox. Jumped over the lazy dog. “; trace(“myMessage: ‘“ + myMessage + “‘“);

myMessage = StringLib.replace(myMessage, “brown”, “red”);

trace(“myMessage.replace brown==>red: ‘“ + myMessage + “‘“); myMessage = StringLib.trimWhiteSpace(myMessage); trace(“myMessage.trimWhiteSpace: ‘“+ myMessage + “‘“);

11.Choose Control Test Movie to try it out.

12.Switch to the AS file and add the following two methods after the trimWhiteSpace() method but just before the final closing class bracket:

public static function countWords(sourceString:String):Number

{

var tempString:String = StringLib.trimWhiteSpace(sourceString);

while (tempString.indexOf(“ “) >= 0)

{

//Replace double spaces with single spaces throughout

//the string. Keep doing it until groups of more than one

//space are whittled down to one space.

tempString = StringLib.replace(tempString, “ “, “ “);

}

//Convert to an array, dividing up the array using the

//spaces as the separator

var wordArray:Array = tempString.split(“ “);

//The length of the array will match the number of words

//in the original message.

return wordArray.length;

}

public static function countSentences(sourceString:String):Number

{

//Look for any “. “ combinations. By looking for a dot followed

//by a space, numbers with decimals will not add to the number

//of sentences, nor will the last period count.

var sentenceArray:Array = sourceString.split(“. “); return sentenceArray.length;

}

13.Switch to the AS file and update the contents of the Actions panel:

var myMessage:String = “ The quick brown fox. Jumped over the lazy dog. “; trace(“myMessage: ‘“ + myMessage + “‘“);

myMessage = StringLib.replace(myMessage, “brown”, “red”);

685

Chapter 27

trace(“myMessage.replace brown==>red: ‘“ + myMessage + “‘“); myMessage = StringLib.trimWhiteSpace(myMessage); trace(“myMessage.trimWhiteSpace: ‘“+ myMessage + “‘“); trace(“myMessage.numWords: “ + StringLib.countWords(myMessage));

trace(“myMessage.numSentences: “ + StringLib.countSentences(myMessage));

14.Choose Control Test Movie to try it out.

How It Works

This example shows how creating a class consisting of static methods is an easy way to create your own library of common code. This class can be easily given to someone else, and it will integrate cleanly into their code, unlike adding methods directly to a built-in class. It is considerably more straightforward to develop and use than subclassing, and you can clearly see when a library method is being used and where the library method comes from.

Composition

Another alternative to subclassing is a technique called composition. This technique involves one class that contains one or more instances of another class but does not inherit anything from that class.

As an example, say that you want to add the ability to create an effect for a movie clip. With the subclassing approach, you would create a custom class that inherits from the MovieClip class and then add the effect to the class. With composition, you create a class that contains a movie clip, but does not inherit from the MovieClip class.

To understand the distinction, think about how the classes you are working with are related. Subclassing uses what is called an is-a relationship, whereas composition uses a has-a relationship. Say you have a Vehicle class and a Car class. The car is-a vehicle, so the Car class can be a subclass of the Vehicle class. Now say you have a Car class and an Engine class. It does not make sense to say that a car is-an engine, but it makes sense to say that a car has-an engine. The car is composed of an engine, brakes, and other parts, so the Car class is a composition.

Figure 27-2 shows the difference between subclassing and composition.

Subclassing

Composition

Car

Vehicle

is-a

has-an

Engine

 

 

Car

Figure 27-2

686

Creating Custom Classes

Some examples of using composition to create a class include the following:

A class called SpinEffect that creates an animation effect for a movie clip. The SpinEffect class has-a movie clip.

A class called RecordSetLoader that uses the XML object to load data from a server and then converts it to a more useful data format. The RecordSetLoader class has-an XML instance.

A Queue class that uses an array to keep track of data to be processed. The Queue class has-an array.

In many cases where an individual chooses to use subclassing, an is-a relationship does not actually exist. Even if such a relationship does exist, many times it is better to re-phrase the relationship to make use of composition instead.

Take a look at actually implementing a composition-based class.

Try It Out

Using Composition to Create a Spin Effect

This exercise shows how to create a class that uses composition. It takes in a movie clip as a constructor argument and animates whatever the passed-in movie clip contains.

1.Create a new ActionScript document by selecting File New and choosing ActionScript File from the New Document panel.

2.Select File Save As and choose an appropriate folder for the file. Give the file the name

SpinEffect.as and save it.

3.Enter the following code into the new ActionScript file:

class SpinEffect

{

private var pTargetClip:MovieClip;

public function SpinEffect(targetClip:MovieClip)

{

pTargetClip = targetClip;

}

public function zoomOut():Void

{

pTargetClip._rotation = 0;

pTargetClip._xscale = pTargetClip._yscale = 100; pTargetClip._alpha = 100;

pTargetClip._visible = true; pTargetClip.onEnterFrame = function()

{

this._rotation += 31; this._xscale *= 0.92; this._yscale = this._xscale; this._alpha = this._xscale; if (this._xscale < 2)

{

this._visible = false; delete this.onEnterFrame;

}

687