1. Code
  2. ActionScript

AS3 101: OOP – Additional Concepts

Read Time:87 minsLanguages:
This post is part of a series called AS3 101.
AS3 101: Quick Tip – When to Use Static Properties and Methods
Everything You Could Possibly Want to Know About Import Statements*

For this mega-installment of our introduction to Object-Oriented Programming in ActionScript 3, we'll take a look at a few outstanding topics, as well as putting everything together and organizing a simple project around several independent, yet cooperative, objects.

If you've been following along with the AS3 101 series so far, you'll have no problem with the general techniques involved in this tutorial. You'll get to focus on how the various elements of the entire Flash movie interact with each other and learn the fundamentals of building Object-Oriented applications. If you find this is a bit over your head, I would advise you to backtrack and brush up on the other topics covered by this series, namely those involving working with XML, loading external images, and the first two parts of this OOP series.

Step 1: Welcome Back

I'm going to start this tutorial by talking about about a few concepts that didn't make it into the previous OOP tutorials. The idea of static members, packages, imports, source paths, and composition are all going to be necessary for both the image viewer project as well as your general on-going Object-Oriented Programmer lifestyle. As with most of my AS3 101 tutorials, we'll focus largely on the concepts and the theory first, and then utilize them in a more practical manner later.

Step 2: Static Cling

All of the properties and methods that we have been writing so far are known as instance properties (or instance methods, as appropriate). What does that mean? That the properties and methods belong to the instance. Remember how we said that all houses built from the same blueprint have a front door, but that the characteristics of the door might be unique on a per-house basis? Yeah, that was a long time ago, way back up at the beginning of the first OOP tutorial, but that's what an instance property is. Each instance can have its own, unique value stored in that variable. It's possible to give one Button101 instance a label of "Abracadabra" and another instance a label of "Hocus Pocus". Because each object has its own property, each instance has its own value stored in it.

However, what if you just need a place to store a value, a value that doesn't need to change independently with each object that is instantiated? What if Button101 had a corner radius property that you wanted to keep consistent across all button instances, so that all buttons had a consistent appearance?

If that's the case, then why does every instance need its own copy of the same value?

It doesn't. If we're confident that all instances will be able to share the same value, then we can have all instances actually share the same property. We do this by declaring the property as being static. That means that the property (or method, if you were to do the same thing to a method) doesn't belong to the instance, but to the class. This is like stating on the blueprint that all houses must have a red-painted wooden door.

What this looks like in practice is this:

All we did is add the static keyword. That turns it into a class property (or, again, a class method). These are also called static properties (or... yeah, you get the idea).

Note that, like the override keyword, the order between static and public/private/internal/protected is inconsequential. Again, though, it's wise to pick a standard and stick with it.

If we do this (declare the property as static, not pick a standard), then all instances will have the same value for cornerRadius. This isn't a copy of the value; this is actually a reference to the same value across all instances.

Why do this? Well, for one, there are places where it just makes sense. In the scenario outlined in this step, it makes sense, because we never have a variation in that particular value. For another, declaring properties or methods as static can reduce memory usage. Since the property points to a single value in memory, and not unique instances of similarly-named values, there is a smaller impact on memory. Obviously, in our simple example, creating a single Number variable instead of two is hardly worth considering, but in cases where objects are created several times over and the value contained within a property is a significant data structure, then static properties might produce a meaningful savings.

Keep an eye out for a tangential discussion of the when to use static methods and properties in the near future.

For a more in-depth look at when to utilize static methods and properties, look out for a soon-to-be-published Quick Tip on Activetuts+.

Step 3: Packages

Another leftover is the notion of packages. We've been blithely unaware of packages until now, but I think you're ready to handle it. Packages are just a way of organizing your code intro group and sub-groups. And it has a lot to do with that package keyword that starts off every class file.

Technically, we've been using packages all along. It's impossible not to; that package keyword defines the package to which your class belongs. However, we've been using what's called the top-level package. When you write this:

You're placing your class in the top-level package.

For various reasons, though, you might want to start putting your class files in folders. Organization is probably the most common reason; you'd like to keep various aspects of a project grouped together and segregated from one another, as necessary. You may also wish to reuse a class name. If you could keep the Main class of one SWF in a separate folder from the Main class of another SWF, then you could use the name twice.

Packages are just the way your classes are organized into folders. It's a two-step process: first you need to create your file in a folder structure reflecting the organization you desire. Then you mirror the folder structure after the package keyword in your class file.

Step 4: Create an Example Package

We'll create a sample package right now, to illustrate how this is done. We'll actually retrofit the previous tutorial's project to use packages. If you don't have those files handy, a copy of it can be found in the download package as "button_tutorial."

In your file system, create a folder to house the entire project.

Copy the Flash file into this folder

Create a series of folders within that folder. They should follow this structure:

  • [project folder]/
    • as3101/
      • sample/
      • ui/

Now, in the as3101/sample/ folder, copy the DocumentClass file . Copy the Button101 class to the as3101/ui/ folder.

That's the first part; we've got an organizational scheme on our filesystem. Now we need to edit those two files so that the package matches this folder structure.

Open up DocumentClass, and edit the first line to read:

Similarly, open up Button101 and make the first line read:

In other words, we edited the files so that the bit between package and the opening curly brace reflects the folder structure leading to the class file itself, relative to the Flash file. We use dots to separate the folder elements, though, not slashes or backslashes. Note that the class name itself is not included; the package is just the enclosing folders. The class name is still listed after the class keyword.

As an organizational technique, this opens up many more possibilities for structuring your projects. As you write more and more classes, you'll want to group related classes together and create hierarchies of groupings. Packages will let you do just that.

However, we're not done here; if you try to run the project right now you'll get nothing; because we've moved the DocumentClass, the Flash file can't find it anymore.

Step 5: Fully-Qualified Class Names

One subtle point that's worthwhile to keep in mind is that as soon as you use a package, your class name got more complex. Now, for all intents and purposes, our two classes are still called "DocumentClass" and "Button101." When you discuss them with other team members, you can refer to them by those names, and even in code you can still use those names. However, technically, the use of a package turns the class name into a longer name. The name becomes package + "." + class name. That is, our DocumentClass is really as3101.sample.DocumentClass, and Button101 is really as3101.ui.Button101.

This is known as the fully-qualified class name, and it will play an important part in organizing our class files.

You may also see it in this form: as3101.ui::Button101. This doesn't really mean anything different from the all-dot version. Just know how to read it (you'll see this most commonly when runtime error occur, the error describes the classes in which the error occurred).

So, why does our Flash document now do nothing? Because, in the document properties, the Document Class is set to DocumentClass. But our class is actually called as3101.sample.DocumentClass. Normally, this kind of mis-naming causes a compiler error, but in this situation Flash is trying to be helpful. It knows to look for a class called DocumentClass to be the Flash file's document class. But it can't find it (that exact class name no longer exists since we've moved the class to a package). So, Flash silently creates a document class, called DocumentClass for us. As it happens, this class does nothing except extend MovieClip and be called DocumentClass. Therefore, if we hit "Test Movie," we get nothing; an empty SWF, reflective of the empty document class created for us.

Before we correct this, first prove this by opening up the publish settings (File > Publish Settings…) for the document. Click on the "Settings…" button next to the "Script: ActionScript 3.0" control. In the resulting window, click on the checkmark button next to the "Document class:" input:

The ActionScript 3.0 Settings window; click on the checkmark button.The ActionScript 3.0 Settings window; click on the checkmark button.The ActionScript 3.0 Settings window; click on the checkmark button.

This will result in the following error message:

The error message when Flash can't find the document class.The error message when Flash can't find the document class.The error message when Flash can't find the document class.

This is Flash's way of saying the same thing I just told you. Only Flash won't tell you that unless you take some kind of action like we just did.

To set things right, change the value of that Document class field to as3101.sample.DocumentClass. When you press Return, nothing should happen (the other option is to get a similar warning dialog). When you click the pencil icon, you should see the class open up. These both mean we have successfully re-linked our Flash file to its newly re-named document class.

However, if you test now, things still don't work. On the bright side, you will at least get some errors saying why things don't work:

Compiler errorsCompiler errorsCompiler errors

In effect, we're experiencing the same problem with the Button101 class as we were with the DocumentClass.

Step 6: Imports

Once you start putting classes into packages, you suddenly need to deal with telling Flash where to find them. Be wary of the issue uncovered in the last step, but fortunately everywhere else the matter of telling Flash where to find classes is a simple line of code. This is the import statement.

You've already used them, and can probably already guess why they're there. We've written quite a few import flash.display.Sprite; sorts of lines in the last two tutorials, but now we get to write them for our own classes.

The error produced at the end of the last step said something about a type not being found: Button101. That might be a little confusing (and admittedly, Adobe's compiler errors tend to be worded in a less-than-helpful way). This just means that we've used something called Button101 but never defined what that is. We're using a class, but we're not using that fully-qualified name to point Flash in the right direction.

You might assume that all you need to do is something like this:

It's not that there's something wrong with using the fully-qualified name, but it does not solve the problem at hand. In order to let Flash know where this newly-relocated Button101 class is, we need to import it.

import lines need to go before the class declaration. That it, after the package opens, but before the class opens. Like this:

They can be in any order you like, but they need to be grouped in that spot. Most developers take the time to alphabetize their import statements (in fact, Flash Builder does this for you).

Now, to solve our current problem, we just import the Button101 class:

Note that once a class has been imported using its fully-qualified name, it is available to the class by it's short name. So, the lines that read var button:Button101 = new Button101(); don't need changed. There's nothing wrong, per se, with using the fully-qualified name at this point. But the import allows us to use the shorthand name, which means we have less characters cluttering up the code, which is a good thing.

Test the movie now, and, with Flash aware of where the various classes are located, you should be back in business.

One little extra note on imports: you technically don't need to write import statements for classes that are in the same package as the class doing the importing. It won't hurt anything to include them, though, and in my mind, the self documenting nature import statements makes that a worthwhile practice.

The finished files for this example (including the package change, the inclusion of imports, and the updated FLA to reflect the new Document class) are in the download package as "button_tutorial_packages".

To summarize these points on imports:

  • If the class you want to use is in a different package, you must use the import statement.
  • This allows you to use the "short name" of the class, but it's OK to use the fully-qualified name if you need to.
  • If the class you want to use is in the same package, the import statement is not required, but it's OK to use it if you want.

Step 7: Wildcard Imports

Now, you should know there are two ways to import a class. One is the way we just did: list the class using its fully-qualified name.

However, you may opt to use the wildcard import. It looks like this:

And it does what you probably expect it to. Any class inside of the ui package is now available to the class doing the import by its short name.

There are a few things to note about this technique.

  1. You can't do this: import as3101.*;. That won't get you anything. Wildcards only import classes that are direct children of
    the package specified. Since Button101 and DocumentClass are both members of sub-packages of as3101, we would achieve nothing by
    that attempt.
  2. Using the wildcard does not automatically compile every class in the package into your SWF. I repeat: it does not.
    The Flash compiler is smart enough to compile only the classes that it sees are being used by your SWF. You do not need
    to worry about needless filesize increases by using the wildcard.

Knowing this, using wildcards or not should become a personal preference. Personally, I only use them on the Flash Player classes (the ones that start with flash.…). In those cases, I use so many or flash.display classes that it's just easier to make sure they're all available without having to trot back to the import section and add a new import. However, with the classes that I write, I find it useful to specifically list the imports individually, as a form of documentation. You can look at the import section and see on which other classes this particular class relies. The choice is up to you, however, and I'd advise you determine your convention and stick to it.

I will be writing a Quick Tip that delves deeper into imports. That will be released before long, so keep your ear to the ground if you'd like to know more about the subtler details of import statements.

For a deeper discussion on wildcard imports keep an eye out for a coming Activetuts+ Quick Tip on the subject..

Step 8: Source Paths

If we're going to talk about using packages to organize our class files, we should then extend the discussion to source paths. A source path is a directory in which Flash will look for classes to compile. By default, every Flash document knows to look in the Flash document's own directory for class files. This is why our examples have worked so far; our classes have always been in the same folder as the Flash document (keep in mind that our recent venture into packages technically puts the class file into another folder, but that the fully-qualified class name resolves the class to a series of folders plus the file. And the root folder of our packages is in the same folder as the Flash document).

Now, there are any number of reasons why you'd want to store classes in some other directory. Two of the most common are:

  1. You have reusable utility classes (possibly TweenMax, Away3D, or something useful you've written). You want to use these classes
    across all projects, and don't want to have to copy the files from one project to the next.
  2. You would like to organize your project so that, for whatever reason, your Flash files are in a different directory from your class
    files. This actually makes sense when you have a large project involving many Flash files. Just to keep the project structure
    clearer, you might decide to keep a "classes" folder and a "flas" folder. This way Flash files are grouped together, but can still
    share classes common across the project.

Naturally, you can accomplish these tasks. I wouldn't have brought them up if you couldn't. I'm mean, but not that mean.

And, of course, you have two options for doing this. Both involve defining a source path. You can define them at the application level (in which case the source path is available to every Flash document you open on that machine), or at the document level (in which case the source path is only available to that document). In the case of reason #1 above, the application level source path makes sense. As for reason #2, the document level source path would be better.

To Define an Application-Level Source Path

Open your Flash preferences (on the Mac: Flash > Preferences; on the PC: Edit > Preferences). Click on the ActionScript category on the left. At the bottom of the resulting window, click on the "ActionScript 3.0 Settings…" button.

Getting to the Application-Level source path windowGetting to the Application-Level source path windowGetting to the Application-Level source path window

In the resulting window, there are three areas: source paths is the top one.

the Application-Level source path windowthe Application-Level source path windowthe Application-Level source path window

Click on the "+" button and type in your path. Or click on the folder button to browse to it. However you enter it, once it's there, click OK until you're back to Flash. At this point, you can freely import and use any class from that directory.

To Define a Document-Level Source Path

With the document open, open the Publish Settings (go to File > Publish Settings…). Click on the "Flash" tab. Click on the "Settings…" button next to the ActionScript version control. A new window opens up, and this will be very similar to the window you get with the application preferences. It's layout is a bit different, but the idea is the same. Make sure you're on "Source Path" and type in your path. Click "OK" and remember that this is for this document only.

The Document-Level source path windowThe Document-Level source path windowThe Document-Level source path window

Between packages and source paths, you have many options for organizing your classes.

Step 9: Composition

In the previous tutorial, we focused quite a bit on inheritance. There is a technique that might be considered oppositely-wound from inheritance. It's called composition.

Now, there is nothing wrong with inheritance. It's not like I sat of a throne of lies and fed you malicious taradiddle in that last tutorial. But you might come across the phrase "prefer composition over inheritance" in your Object-Oriented journey. If (when) you get to learning design patterns you will most certainly hear it, and you will even see it manifest if you hang around advanced coders long enough. Big Spaceship recently (as of this writing) posted a discussion of the methodology they used when designing the display package of their github-ed ActionScript library. In it, they explain their reasons for, essentially, composition instead of inheritance.

So what is it? The good news is that you already know how to do it, and have been doing it all along. Composition is just when one object stores another object in a property. If your class has a property called myLoader, and stores in it a Loader object, that your class is said to compose the Loader object. This happens all the time; the Loader itself, in fact, has a contentLoaderInfo property that, itself, composes a LoaderInfo object, which gives you information about the actual loading.

Step 10: The Subtle Difference

Composition seems like a far cry from inheritance, doesn't it? The two probably don't seem related at all. But consider this: In the last tutorial, we spent most of the time making a Button101 class that extended Sprite. This made it everything that Sprite is (and everything a DisplayObject is, and everything a DisplayObjectContainer is, and…, well, you remember that discussion). This was certainly useful, and again, I'm not here to tell you that it was the wrong way to do it.

But an alternate way to do it would be to not extend anything in particular, and instead let the Button101 class instantiate its own Sprite object, or receive a target Sprite object as a constructor parameter, or in some way compose the Sprite and not inherit it.

How this would work may not be immediately clear. Allow me to throw a bunch of code at you in a shotgun-like effort to make this more clear.

First, let's consider the original implementation. Here is, in full, the Button101 class from the last tutorial (you may find the project in the "button_tutorial" folder in the download package):

And here is the snippet of code from the DocumentClass class that set up a Button101:

Now, here is equivalent code, only utilizing composition instead of inheritance. This is the updated Button101 class (changed and added lines are highlighted):

And here is the updated snippet of code required to set up the updated Button101 (again, changes are highlighted):

It's not much more code. It's more about the addition of the _target property and the usage of it instead of the implicit this where Sprite-like things were concerned.

Here's a more thorough breakdown:

  • First, on line 8, we remove the extends Sprite bit so that we're not utilizing inheritance.
  • On line 10, we added the target property. This will store our Sprite (that is, it composes the Sprite) that was previously
    being inherited. This sets up with composition, and not inheritance.
  • On line 17, the first line of the constructor, we create a new Sprite and stash it in our _target property. At this point,
    we're successfully using composition.
  • On lines 22, 33, and 35-39, we've simply added a reference to _target in front of method calls that were previously supplied by
    the Sprite superclass. Because we're not extending Sprite anymore, we no longer have an addChild or addEventListener method,
    or mouseChildren or buttonMode properties. We do, however, have a Sprite on which we can call those things, so we transfer the
    responsibility there.
  • In lines 67 and 71, we need to remove the override keyword, as we're not extending anything, so there's nothing to override.
  • At the end of the class (lines 77-79), we add a whole new method. It's a getter function that returns a reference to the
    composed Sprite. This step is crucial for the changes in DocumentClass.
  • In DocumentClass, the changes all involve directing Sprite-like behavior to the target of the button, rather than to the button
    itself. For example, on lines 2 and 3, rather than positioning the button object, we position the target of the button. Similarly,
    we can't add button as a child to the display list, but we can add

Then end result should be identical. The difference is under the hood, in how that Button101 is set up.

Actually, the end result doesn't work without some extra modifications, as the click handler needs a little extra attention. Explaining the required changes is a bit beyond the scope of this tutorial, but you can find a fully-working example in the download package under "button_tutorial_composition"

Step 11: "Has-a" and "Is-a"

So, if both inheritance and composition are acceptable techniques, how do you know when to use which? Well, first, there's the archetypal answer, mentioned earlier, of "prefer composition over inheritance." If you feel you have the option of going with either, you might want to err on the side of composition, if only because you know some curmudgeony old tutorial writer who told you do so. In all seriousness, though, you can trust that people smarter than you (and smarter than me) have come up with that preference idiom, and there's no reason not to take their word for it.

But how about some more practical advice? A popular technique for evaluating the appropriateness of inheritance versus composition is the "Has-a"/"Is-a" test.

It goes like this. You take the names of your two classes (for the moment we'll pretend they are "ClassA" and "ClassB") and try them out in the following sentences:

ClassB is a ClassA.

ClassB has a ClassA.

And take note of the one that is a more appropriate relations.

If is a is more appropriate, then the relationship is one of inheritance. If has a is more appropriate, then it's a compositional relationship.

For example, let us consider the built-in TextField class. Say the following out loud (loudly, and with conviction, especially if you're in a crowded room).

A TextField is a DisplayObject.

A TextField has a DisplayObject.

To me, at least, the is a relationship makes more sense. The TextField is displayable, so it is a DisplayObject. Now TextField's can also style their text, by way of the TextFormat class. What about the following:

A TextField is a TextFormat.

A TextField has a TextFormat.

At this point, we hopefully agree that it makes more sense for the TextField to utilize (have) a TextFormat. It's a useful component of a TextField, but a TextField isn't, itself, defined by being a TextFormat.

Let's think about another example – the built-in Loader class. Keep in mind that Loader is a type of display object that can load external content. Let's try out the following statements:

A Loader is a DisplayObject

A Loader has a DisplayObject

If you answered that the is a relationship makes more sense, you're right. The Loader is, itself, displayable. If you answered that the has a relationship makes more sense, you're also right! There are no losers here at AS3 101 Camp! The Loader has a property called content which is the displayable content that was loaded.

One more example, then we'll drop this theoretical discussion. Let's use our previous Button101 in the test:

A Button101 is a Sprite.

A Button101 has a Sprite.

Well, this is our debate from the previous step, isn't it? Which makes more sense? To me, either makes sense. The button could very well be a DisplayObject of some type, or it could be something else and merely control the DisplayObject in question. In this particular example, I think the answer depends a little on personal preference. For the record, my preference is to favor composition. You may draw your own conclusions, but remember that every time you choose inheritance, God kills a kitten.

(Yes, that was a joke. Personally, I don't believe in kittens.)

You can learn more by searching for "inheritance versus composition" or "prefer inheritance over composition." Many of the results I spotted were on the intermediate-to-advanced technical side, but if you crave a deeper discussion on this topic, Google can help you out.

Step 12: The Ultimate Mnemonic Device

If you need a more visual explanation of Inheritance and Composition, then consider the following.

Inheritance is like Robocop. He is a typical human extended with extra functionality. Or put another way, he is a robot that inherits core functionality from a human.


Composition is like Michael Knight and KITT. Michael Knight is a typical human, and remains a typical human, only he owns (composes) a self-aware car.

Knight RiderKnight RiderKnight Rider

Both are totally rad, but they've arrived at their respective awesomeness through different techniques.

But of the two, only the Hoff went of to star in Baywatch. Therefore, prefer composition over inheritance.


(I should point out that I did not come up with this analogy, but I am at a loss for locating the original source. If anyone knows, please let us know in the comments)

Step 13: Communicate

My point in bringing up composition has more to do with assembling a larger program out of objects, rather than getting into a discourse on the finer points of whether it's appropriate over inheritance in various situations. Those finer points are important, and you'll be running into them many times over as you grow as an Object-Oriented developer, but right now we need to see how we put our Object-Oriented Programming techniques to use in building a larger application.

The most common frustration and confusion I see in budding OOP developers is over the communication between various objects. Most people at this point in their education understand classes and objects, and how to write a given class. The next step is to start using multiple classes and objects to create something useful (like a website), but it seems that the natural confusion comes from learning that objects are meant to be encapsulated (remember that discussion? It was covered in my first OOP tutorial). Then how does one object talk to another object, so that a system can be created, out of which that useful program can be built?

Most first attempts I've witnessed involve either terrible, terrible violations of the encapsulation principle, of the scope of responsibility for a given class, or both. Or else the attempts simply do not work. I would like to take the time (and article space) to show you an example of a poorly-written system of objects.

Step 14: The Code In This Step is Questionable, Please Do Not Bother to Type It Out For Yourself. We Are Illustrating Less-Than-Ideal Technique.

I hope the heading made the point.

In this example, I present two classes, Button101BadIdea, and DocumentClassBadIdea. They are based on the previous Button101 examples, but you will see modifications specifically meant to illustrate the bad technique. I've stripped down both classes, though, in an attempt to bring a little focus to the relevant code.

Here is DocumentClassBadIdea

And here is Button101BadIdea:

The change is subtle. And if you try this out (look in the download package for the "bad_idea" project), you'll see that it works just fine. But here's what changed:

  • In DocumentClassBadIdea, on lines 18 and 24, there are two arguments passed to the Button101BadIdea constructor, a reference to the TextField on the stage, and a bit of text.
  • We also removed the lines that added a CLICK listener to the buttons, and the click-listening function.
  • In Button101BadIdea, on line 16, we're receiving two parameters (the TextField and the String), and then storing them in instance properties on the next two lines.
  • At line 38, we add our own CLICK listener, to be handled internally.
  • On lines 55-57, we have the CLICK listener. This uses those new properties to apply the String to the text value of the TextField.

Now, if it works, why is it a bad idea? It's usually a pretty subjective thing to deem the correctness of code. On the one hand, if it works, then it can't be that bad. On the other hand, if the code is to be maintained, then the maintainability of the code is just as important being functional.

The code presented here is not maintainable. The biggest problem is right here (in Button101BadIdea):

We have just locked our button class into doing one thing and one thing only: putting text into a TextField. Now, granted, it's been set up to use any text and any TextField, but really this button has a very specific purpose. If you ever needed a button to do something else – anything else – you'd be stuck with having to make more button classes. Even if the appearance of the button was to be the same.

Step 15: I Can Has Better Solushun?

Of course, the better solution was the one already presented in the previous tutorials (see the "button_tutorial" project in the download package if you need a refresher). But what makes it better?

For starters, the Button101 class is far more reusable if it doesn't assume that a click should result on putting text into a TextField. If needed, that end result can still be accomplished, but so can any other result. But we achieve the result not within the Button101 object; instead, that logic is in the DocumentClass object. Or, more generally, that logic is in the object that "owns" the Button101 object.

Now, the object that owns the Button101 object can be pretty much any object that requires a button. The need for the button is then followed by the specific end result needed when the button is clicked. And that's the responsibility of the object that needs the button, not the button itself.

So what we have, in the original and better solution, is one object that composes another object. The object being composed is rather autonomous, but provides certain public properties and methods with which it can be customized to a degree. The object doing the composing is in control, and manipulates the composed object through its public properties and methods.

This relationship is the basic building block of larger applications, and the focus of the remainder of this tutorial.

Step 16: The Hierarchy of Composition

To enable two-way communication between classes, we need to start thinking in terms of hierarchies of composition. As a rule, if object A composes object B, then the reverse is decidedly not true. I must iterate that, naturally, there are exceptions to this rule. But this particular methodology works well in a vast majority of cases, and fits in nicely with the built-in event system in ActionScript 3.

Think of it this way. Your SWF starts with a document object. This object will, among other things, be composed of other objects, such as MovieClip, or objects representing MovieClips. There may even be an object that represents a collection of thumbnails or a grid of buttons. The object may be "owned" by the document object (by "owned," I mean the document object composes the thumb collection object). Now, the thumb collection object might compose more than one thumbnail object, which themselves are composed of a Loader object and a TextField object.

Now, does it make sense for the thumb collection object to compose the document object? Or for individual thumbnails to compose the thumb collection object? Is it even possible for a built-in class like Loader to compose a custom class like Thumbnail?

A hierarchy of composition starts to become apparent. You'll notice that not only is there a structure to how things are composed, but the objects themselves start at the most grand (the document object) and work their way to more and more specific and focused objects (such as a thumbnail object, or the basic building block of a Loader object).

We can illustrate this hierarchy like so:

A hypothetical hierarchy, illustrated

In the above diagram, objects are represented by the boxes with the class names in them. One object composes another if the line connecting ends with a diamond shape at the composing end. That is, in the above illustration, DocumentClass composes Thumbnail, and Thumbnail in turn composes Loader and TextField.

Step 17: Embracing Events

ActionScript 3 is largely based upon the idea of dispatching and listening for events. I won't get into the basic of events here; you can read up on this ActiveTuts tutorial for more information if you need it. Events are a great way to enable communication (that's pretty much what they're about). Dispatching an event from one object allows pretty much any other object to act in response to it.

However, note the difference between executing public methods and handling an event. For instance, imagine object A and object B. Object A can execute any of object B's public methods, and that's one form of communication. If, on the other hand, Object A dispatches and Event for which Object B is listening, then that's another form of communication. But note the difference. The first is active: Object A directly manipulates Object B. The second is passive: Object A merely dispatched an event. Object B may not even be listening, and if it is, Object A has no control over what Object B may do as a result.

Step 18: Open Communication

So we have a hierarchy of composition. Typically the document object composes the thumb collection object. And because of that relationship, the document object gets to execute public methods on / directly manipulate / actively communicates with the thumb collection object. But the thumb collection doesn't know about the object that "owns" it. Ideally, it would be written in a reusable way so as to allow it to be used by any other object that wants to use a thumb collection. So we can't tie it to any particular document object.

But we want the document object to remove its preloader when the thumb collection is all loaded; how does the thumb collection communicate with the document object if it doesn't compose it? As you may have guessed, it does so through events. The thumb collection communicates indirectly / passively with the document object. In fact, it's a bit misleading to say it "communicates with the document object". It's really just saying, into the void, "My images have loaded, in case anyone cares." And thankfully there is someone listening in the void, and they do care, because they will now remove their preloader.

In other words, two-way communication is often achieved through a public method in one direction, and through event dispatches in the other.

Now, perhaps the most common form of two-way communication that's not the method just described is the ability for a public method to return a value. I would argue that this isn't so much two-way communication as it is a way to get information out of another object. That is, if Object A calls a method on Object B and gets a return value, then Object B may be told to do something, but Object A isn't really expected to do anything in response to getting the value back. It's splitting hairs, and while this maneuver is valuable, it's not our focus here.

We'll be picking this apart in a more hands-on approach momentarily, but keep this concept in mind as we build our image viewer.

Step 19: Building an Image Viewer

Let's roll up our sleeves. First things first, let's get an overview of our project. The final example was shown at the top of the article, so if you forget, trot back there for a moment. This step will be about defining our needs and breaking apart our objects.

First, let's write down the objectives

  • Display a group of thumbnail images
  • Clicking on a thumbnail image will display a larger version of the image
  • This will also cause a "selected" state to appear on the thumbnail
  • Clicking on a "next" or "previous" button will cause the appropriate image to show in the larger area, as well as selecting the appropriate thumbnail
  • If the user is at the first image in the sequence, the "previous" button will be disabled and display itself as such. Similar action with the "next button" at the end of the sequence.
  • Clicking on the larger image results in a link to the original image on Flickr
  • Control which images are shown, and the associated data, through an XML document that is loaded at runtime

You may be able to tell, but I was drawing from this particular project in the last few steps with my examples of composition structures. Call it foreshadowing. So this following list of needed classes shouldn't be a total surprise:

Document Class
"Runs" the SWF. Pulls everything together into a working application

Manages a group of Thumbnail objects. Responsible for creation and layout of individual thumbnails, based on incoming data. Also responsible for selecting thumbnails.

An individual thumbnail. Acts as a button and loads and displays a image. Can also display a selected state, which can be enabled or disabled through public methods.

Loads a larger image. Can be clicked, and dispatches that event.

Controls the two PaginationButtons, re-dispatching their CLICK events and managing when they are enabled and disabled.

An individual button of the "next" or "previous" variety. Basically just a button, although they can be enabled and disabled, and update their appearance accordingly.

All of the other classes mentioned so far have been visual; this one is not. It's responsibility is to load the XML file and parse it, and retrieve relevant data when asked. This kind of class tends to feel more foreign to beginning Object-Oriented programmers, but it's no more or less useful than the visual side of things. It's inclusion here is partly to illustrate this point.

A Value Object whose sole purpose is to carry bits of data pertaining to a single image (A "Value Object" is just an object with, typically, a bunch of properties and few if any methods. The point is to collect some bits of information – values – into a single object).

We'll need a custom event class in order to package up some useful information along with the event. The event will specifically be when an image should change due to some kind of interaction.

With that in mind, let's get started.

Step 20: Create the Project

We have a good idea of the classes involved, so we can successfully create the project. I'll detail the process using the computer's filesystem and OS, but if you're comfortable with a good project-oriented code editor, such as Flash Builder, FlashDevelop, or TextMate, feel free to create the project using those tools.

First, create a project folder. I'll call mine Image Viewer.

Inside of this folder, create two more folders, "src" and "bin". "src" will be where we put source files, like FLA files, AS files, and anything else that doesn't need to be deployed to run the final product. "bin" will be where the various files are that get deployed, such as SWF files, XML files, and image files that get loaded by the SWF.

Inside of the src folder, make two more folders, "flas" and "classes". We'll only have one FLA file, but we'll aim to organize our project by keeping FLA file separated from AS files.

Next, we'll create some folders in classes to be our package structure. Create this structure inside of classes:

And lastly, create some more folder in bin to help keep that organized. We need folders for xml and images, specifically.

The final folder structure should look like this:

The whole project's folder structureThe whole project's folder structureThe whole project's folder structure

Step 21: Set Up the FLA

Now, create a Flash file (ActionScript 3.0, of course) and save it in the src\flas folder. I'm calling mine ImageViewer.fla. Alternately, you can start with the FLA file provided in the download pack, in image_viewier_start. It has graphics in it for the various elements so you don't need to burn time drawing buttons and the like.

Even with the starter FLA, we need to set up a few things.

Choose the menu item File > Publish Settings…. Make sure the "Formats" tab is selected, and choose a name for your SWF. More importantly, though, is to set the path. We want to go to the bin, so type in ../../bin/ImageViewer.swf.

Next, click on the Flash tab, and then on the Settings… button next to ActionScript Version. In here, make sure the Source Path tab is selected, and click on the + button.

In the resulting entry, type ../classes/.

Setting a relative path for publish paths and document source paths make the project more portable. Resist the temptation to click on the "target" button, as that results in an absolute path to the classes folder. This makes it much harder to move the project to a new location on your hard drive, or to hand to someone else for them to work on on a different machine.

Step 22: Create and Set the Document Class

Create a new text file and save it as in [project]/src/classes/ui. Add the following to the file:

Note that we need to fill in the package in the class, as we're technically in the ui package. In case you missed it, remember that we don't put classes in the package, as the classes folder is our source path. We start there, but we don't include it as part of the package.

In your Flash file, make sure nothing is selected (press Command-Shift-A) and open the Property window. Where it says Class, enter:

Remember how the use of packages makes our full class name longer? We need the full name in the document class field, so we need to prepend the short name (ImageViewer) with the package.

Now, if all went well, we'd be able to test the movie at this point and see a trace of "Ready." in the Output panel:

The Output panel says it's readyThe Output panel says it's readyThe Output panel says it's ready

At this point, we have a project waiting to be worked on, and we have a document class hooked up to the FLA. We'll soon add more classes, but first, let's get our loadable assets ready.

Step 23: Add the Images

You can find a number of images ready to use in the download pack. You will find them in image_viewer/bin/images/. You can poke through the files if you want, and you'll probably find them predictably organized. There are thumbnail-sized images in one folder, and there are also larger versions of the same images in another folder. Put the small images in your project's bin/images/thumbs/ and the large images in bin/images/detail/.

The images are all culled from Flickr, and are licensed under the Creative Commons. Attributions can be found in the next step, as well as in the final project.

If you wish to use your own images, size your thumbnails to 100x100, and your details to 400x300, and just put them in the appropriate folders.

Step 24: Create the XML File

We'll now catalog those images in an XML document. Flash will consume this file and parse the data it needs out of it. The structure of this file is important for following along with the rest of the tutorial. However, if you've used your own images, be sure to set the content of the XML appropriately. The format is not complex, hopefully you can make the substitutions on your own.

Create a new text file, and save it as images.xml in bin/data/.

Our XML structure is basically just a single list of image items, an individual node of which looks like this:

A single image has an image name, an attribution, and a source web page. Seems reasonable, unless you're wondering how we load an image from just an image name. To reduce typing, we'll stick a few attributes in the root node to define the paths to the thumbs and details folders, like so:

What this does is three-fold:

  1. We won't have to type out the full image path for each image, because we'll assume that all images are in the same directory.
  2. We don't have to specify individual paths for the thumbnail and the detail, and instead we rely on the two versions having
    the same filename, just residing in different directories.
  3. We can easily customize the location of the image folders, if you so desire.

Here is the full XML document, populated with data relevant to the images in the download package:

Step 25: Create the DataLoader Class

We get to start right in with our first non-document class, and it's a non-visual one, as well. Begin by creating a new text document, and saving it as in src/classes/data/.

Now, as this is not a visual class, there's definitely no need to extend Sprite or MovieClip. However, we do need to dispatch events, so it will be beneficial to extend EventDispatcher. Here is our first pass at the code, which is moderately complex. The individual techniques used herein should not be new to you, so I won't belabor the loading and handling of XML. There is a brief walkthrough after the code, though.

In short, the DataLoader object loads an XML file and stores the file as XML once it loads. To get it to load, we need to pass in a URL to the XML document to the constructor. The load starts, and the conversion of raw text into an XML object is handled by the COMPLETE listener. There is also a simple error handler, which will spit out some useful information to the Output panel, but more than anything prevents an IOError from disrupting the program if something bad does happen.

Step 26: Integrate the DataLoader into the Application

With our DataLoader class created (even if it's not finished), we can go ahead and use it. This is a nice way to work: start with a minimal program (such as a FLA and an almost-empty document class), add one object that's barely functional, make sure it works, add functionality, test, repeat. By not typing too much, we can minimize errors. Our frequent tests make it more obvious where errors were introduced, and by starting with zero functionality we progress in complexity in an organic way.

At any rate, go back to the ImageViewer class and have it create and use the DataLoader:

Test it out, and you should see a single trace, indicating that the data has finished loading.

The data is loaded.The data is loaded.The data is loaded.

Step 27: Analyzing the DataLoader

Now, let's look at the choices that we made when designing things so far.

First, we chose to have the DataLoader contain its raw XML in a private property, and we have not written a public getter for it. The reasons for this is to encapsulate the raw data. Our next steps will involve writing some specialized methods to access the data, so we'll be continuing this discussion, but the main point is that we want an object to be responsible for maintaining the data. By restricting public access to the raw XML, we can control how the data is accessed. Not only that, but by writing methods to retrieve data, we can – at a later point – change the underlying mechanism of how the data is structured without the need to update multiple class files in response to that change. We would only need to update the DataLoader class.

Second, we have the DataLoader handle it's own error events. Now, realistically, if errors were to be an actual issue (for instance, if you were loading XML from another domain which may be inaccessible beyond your control), you'd want to handle things a little more elaborately. But for our purposes, we're pretty confident that the XML is going to load. In the case that it doesn't (say, we misspelled the file name), we would get the error event. If the error event isn't handled (that is, there is no listener for the URLLoader's IOErrorEvent.IO_ERROR event), then a runtime error is triggered. By hooking up something, we can at least prevent against disruption to the execution of the program. But the point is that we handle this event internally, so that, when we create the DataLoader in the document class, we don't have to worry about that error so much. The DataLoader is a bit more of black box, encapsulating the internal mechanism of the load.

Finally, we chose to dispatch a COMPLETE event once the XML file loads and is converted into an XML object. This does two things for us. First, by dispatching our own event, as opposed to letting the document class listen directly to the URLLoader's COMPLETE event, we can ensure that the event is our COMPLETE event. That is, it's not enough that the external file has loaded. We also want to make sure we have an XML object instantiated. So we dispatch the event when we're ready, after the XML is stored.

The more important thing that the COMPLETE event accomplishes is the opportunity for two-way communication. It'll work like this: The document class creates a new DataLoader, effectively communicating with it to load a certain file. When the file is loaded, the DataLoader dispatches the COMPLETE event, in a sense communicating with the document class so that it proceed with the rest of its logic.

But this is a subtle point, and I'll continue to point it out: while the document class communicates directly the DataLoader, giving it a specific file to load, the DataLoader merely dispatches an event. The document class may or may not be listening, as far as the DataLoader is concerned. At this point, DataLoader could be used in a completely different project, provided we wanted to utilize an XML file with the same structure.

We have here our first composition hierarchy. The document composes the DataLoader and works with it directly, while the DataLoader is more vague in its communication and simply dispatches events.

The hierarchy of composition thus far

Don't forget, though, that even at this point there is another level of composition, as the DataLoader composes URLLoader and XML objects.

Step 28: Add a Data Accessor Method

Let's give DataLoader a way to expose the underlying data. However, as discussed in the previous step, we'll avoid exposing the XML object and instead create data accessor methods as we need them.

In DataLoader, add the following method:

If you're unfamiliar with standard parsing of XML with E4X, then you might want to brush up on that, as we'll be doing similar things in the coming steps.

The point I want to focus on is that we have created a method that will parse the XML for us, and simply return a list of paths to the thumbnail images. This separates the responsibilities of the objects: The document class is merely going to want that list of paths and not be mired down in the details of parsing the XML for that information. Conversely, the DataLoader class is all about the details of the XML structure and qualified to parse it; so let's put the logic where it belongs.

Step 29: Use the Data Accessor Method

Back in ImageViewer, let's use that getThumbnailPaths method we just created. Modify the onImageDataLoad method to look like this:

This doesn't add much functionality for now, but it does print out something more interesting than before. You should see a list of full paths to the thumbnail images:

A list of image pathsA list of image pathsA list of image paths

This illustrates that our getThumbnailPaths method is working, and also that we're using the composed object by directly calling methods on it.

Step 30: Create a Thumbnail Class

Our next step would be to pass this thumbPaths data to the ThumbnailCollection class so that it can then do its job of managing multiple, individual Thumbnail objects. Obviously, to get it to work, we kind of need to develop the Thumbnail class at the same time. We'll start with the Thumbnail class and then "connect" the document class to the Thumbnail objects through the ThumbnailCollection object.

Keep in mind that it doesn't really work that way; the document class will have no knowledge of the individual Thumbnails because they'll be encapsulated by the ThumbnailCollection. And vice versa, because we'll be dispatching events from Thumbnail to ThumbnailCollection, which will then re-dispatch events as necessary back to the document class.

With that in mind, let's build a Thumbnail class. Create a new text file and save it as in classes/ui/thumbs/. Add the following code to the class:

All in all, a moderately complex class, but nothing terribly advanced. A few points of clarification:

The ThumbnailGraphic class is, if you haven't spotted it yet, a class provided by the FLA as a symbol that has been exported for ActionScript. So, there is no class file for it (although, conceivably, we could have actually made the exported class the Thumbnail class, but our focus is on composition, so rather than extending a DisplayObject we're composing one).

We use a Loader object to handle the loading of and display of an image. We're handling the complete and error events. The error event is a basic trace. The complete handler is a little more involved. However, it's all about merely displaying the loaded image within an 100 x 100 square. If the image is larger than that, then the resulting calculations on the scrollRect will crop the image to the center. If it's smaller, then the image will get centered. This isn't a substitute for not cropping your thumbnail images properly, but it does help prevent unsightly bleeds in the event of user error when providing images.

The mouse hover is straightforward enough, using the highlight frame found within the Flash symbol. Feel free to church it up here, and implement a tween using TweenMax or Tweener or the animation package of your choosing. Animations always add a nice feel to most pieces, but I'll remove the tangent for my purposes.

A CLICK event has been hooked up, but we'll leave the logic of the listener for the time being.

The select and deselect methods will get used a bit further down the line. But we're setting them up here. The idea is that a selected Thumbnail will have the highlight on it (at bit more "solid" of an alpha setting), but also be non-interactive at that point. This will prevent clicks on the same Thumbnail in a row, and keep the highlight from acting unexpectedly. deselect just reverses everything that makes a Thumbnail objected selected.

There is a getter for the container property, which is an instance of the ThumbnailGraphic symbol. This provides access to the display object that will need to be positioned and added to the stage. Being a public function, we'll be seeing this again in the next step as we write the ThumbnailCollection class.

We'll be coming back to this class later, but for now let's turn our attention to the ThumbnailCollection class.

Step 31: Write the ThumbnailCollection Class

The ThumbnailCollection class is, again, responsible for managing multiple Thumbnail objects. Make another text file, and save it as in /classes/ui/thumbs. Here's our initial code to put in:

You might think of this class as a glorified Array. Its main function is to house a collection of Thumbnail objects (hence the name of the class), and it does so in an Array. But it also protects that Array. If we use a ThumbnailCollection object (and we will, in the next step), then we need to trust that it will do its job at managing the collection, and thus we don't have direct access to that Array. We can add items to the Array indirectly, through addThumbAt, but that's about it. Not to mention that that method also does a bunch of other things to create a Thumbnail.

Step 32: Use the ThumbnailCollection Object

Let's put that all together and see what we can do. Back in ImageViewer, we'll create a ThumbnailCollection instance and populate it using the list of thumb paths from DataLoader.

At this point, our composition hierarchy looks like this (a line with a diamond on it means that the class on the diamond side is composing the class at the other end of the line):

The Composition Hierarchy so far

Note that ImageViewer is composing the ThumbnailCollection, and therefore calls methods on in directly. But also notice that the DataLoader and ThumbnailCollection are completely independent of each other (successful encapsulation), yet can cooperate through the mediation of the ImageViewer. Again, the higher-level objects tend to compose the more focused objects, and therefore have knowledge of them. The other side of the coin, though, is that the more focused objects tend to not know anything about the objects composing them, and instead provide the service of dispatching events.

OK, at this point, we should be able to make it work! Test the movie, and instead of traces, we should see some images show up along the bottom of the SWF. You should also be able to interact with the thumbnails, getting that highlight to show up on mouse over.

The Thumbnails loaded, displayed, and interactiveThe Thumbnails loaded, displayed, and interactiveThe Thumbnails loaded, displayed, and interactive

Step 33: A Custom Event

We're going to handle a click on a Thumbnail object. The individual CLICK needs to be detected on the Thumbnail itself, then dispatched out to the ThumbnailCollection object. From there, it determines the index of the Thumbnail that was clicked and redispatches a custom event. ImageViewer will be listening for this event, and use it to determine what happens next.

Let's start with the custom event class. It's going to be a standard Event, only it has a spot for an value property. This will be determined by ThumbnailCollection and used by ImageViewer. Our custom event will also define some custom event types.

Create another new text file and save it as ImageChangeEvent in classes/events/. This will be a typical Event subclass, which we haven't talked about specifically up to now. Full discussion after the code:

First, this class is in the events package. It's not required, but it's common to place your custom events in an events package. This keeps them grouped together, as well as conceptually separating them from the other classes. A custom event, just like a regular event, should be extremely lightweight and not dependent on too much to get its job done. Putting events in their own package reinforces the notion that this is an event that can be dispatched from, technically, any object in the project, not just, say, the objects in the thumbs package (this is foreshadowing, by the way. We'll be dispatching this event again, mark my words).

Next we need to import the built-in events package, specifically Event, because we extend Event. We're using inheritance here because we pretty much only have one choice. In order to create a custom event, we have to subclass Event. Just the way it goes.

The first few lines of the class itself are properties. There is a public static constant, which defines a String that is an event type. Our event will actually only define this one type for now, but it's not uncommon to list several of these types here. This gives us an event type like ImageChangeEvent.INDEX_CHANGE, not unlike something like Event.COMPLETE or MouseEvent.CLICK. The other property is just a private instance variable, in which we'll store the index value of the image to which we're changing.

The constructor is fairly typical for an Event subclass. The type, bubbles, and cancelable parameters are simply transferred to the super constructor. We just need to make sure we do that. The bit of custom work, and really the whole point of creating a custom event, is the value parameter. That's our own addition, so we store that in our instance property and pass the rest of the parameters to the super class.

The next two methods are overrides of methods in the super class. It's not strictly necessary to do this, but it's not a bad idea. The clone method provides an easy way to create an identical copy of a given event. This is generally as simple as creating a new event object using the values stored within the original event, and returning it. The toString method lets us provide informative output if the event ever gets traced. We take advantage of the super class's formatToString method, and just pass in the name of the class and any properties we want to include in the output. We'll see this in action in just a little bit.

Lastly, there is a public getter for the _value property. This provides a read-only property. But this property is going to be extremely useful in our program.

Nothing to test here, so let's move on to actually using this event object.

Step 34: Clicking an Image

So far, we've done pretty well just writing up whole classes and getting them to work together. Our next task involves smaller changes across multiple classes, so pay attention.

We'll start in the Thumbnail, where we'll first listen for a normal CLICK event on the ThumbnailGraphic, and then simply re-dispatch that event.

First, add the CLICK event listener:

Also take it into account in the select and deselect methods:

And then write the listener function:

Turning to ThumbnailCollection, we need to make sure we listen for CLICK events on all Thumbnails, and then dispatch an ImageChangeEvent as a result, after looking up the index of the event-causing Thumbnail.

First, import the ImageChangeEvent:

Then, in the addThumbAt method, make sure to listen for CLICK events:

And lastly write the event handler. This bit might take a little explanation:

The code is actually rather simple, but here's the gist: First, grab the event target and cast it as a Thumbnail. This gives us the Thumbnail object that was clicked. Then, look up the index in the _thumbnails array of that object. The indexOf method does exactly that: it returns the array index of the the object passed into the method. Finally, using that index, we create a new ImageChangeEvent which we then dispatch.

One last thing to do, and that's to listen for and handle ImageChangeEvents in ImageViewer. In that class, import the ImageChangeEvent:

Then we need an instance property to hold onto the image index we've chosen (the reasons for which will be clearer in the near future):

And then update the code to listen for the event in the constructor:

Finally, we need to write that event-listening method. We'll get to more interesting code in the next step, but for now let's write out the method and trace the index:

That was a lot of editing, but actual code added was just a handful of lines. Just keep telling yourself that this is all in the name of the hierarchy of composition.

And we should be able to test it now! Go ahead and run the movie, and click on the thumbnails. You'll see the index of the thumbnail you clicked on show up in the Output panel.

The Output panel after clicking on a few ThumbnailsThe Output panel after clicking on a few ThumbnailsThe Output panel after clicking on a few Thumbnails

For your reference, here are the complete listings of the code so far for the three affected classes:




Step 35: Encapsulate Image Data in a Value Object

To get image data from the DataLoader, it would be convenient to bundle up all of the necessary information (remember, an node contains name, attribution, and link attributes) in a single object. We could use a regular old object, like this:

But this is a tutorial about Object-Oriented Programming, so we're going to add another class. The responsibility will be solely to contain the properties associated with an image. This will be a very simple class, but it will provide us with a strongly-typed value we can use to pass around this information.

Create a new text file, and save it as in classes/data/. Add this code:

The class exists to provide three properties. This means we can transfer the data from the node in the XML to an ImageData object. While we were writing the class, we also added a convenience to the constructor: pass in three arguments, and get those properties set for you. So we can create an ImageData object with one line, like this:

Now, even though this is a very simple class, why go to the trouble and not just use the regular Object? The advantage is datatyping and strict compile time error checking. If you have a variable typed as an Object, there isn't any way for the compiler to know that source is or isn't a valid property of the variable, or that it is or isn't a String. Our ImageData class, however, has only three valid properties, each with its own proper datatype. The compiler will catch unintentional misuses of the object by producing errors.

Not only that, but philosophically, we can think in terms of objects throughout the application. Even something as simple as "data about an image" can represented as an object, and thus we can create a class for just this purpose.

Step 36: Querying the Data for Details

Well, we have an index. We've managed to get the index of the Thumbnail that was clicked, but now what? We need to go back to the data to get more information relating to the index. Basically, we want all information – as an ImageData object – that is associated with the index that we have.

Open up DataLoader and add this method:

Remember, a single node looks like this:

This method is actually rather simple. We take the index that was passed in and use it to find the matching node in the XML data. Then we take that node and get the attributes out of it. With those values, we create an ImageData object and return it. However, note the check to see if the node exists; if we've passed in an out-of-bounds index, we won't have valid data in the XML, so we can't return a valid ImageData object. This is a simple check that can prevent errors down the line, in the rare event that we'll need it.

Put this method to the test by using it in ImageViewer. Start by importing the ImageData class:

In the onImageChange method, we can remove the trace and query the DataLoader for the relevant information.

We'll also pull the logic for doing something with this index into a new method. At the moment, it might seem like overkill to do so, but we'll need to do it later, so I'm using my crystal ball and saving us a little overhead by making the method now.

Test the movie, and you should see a relevant trace when clicking on thumbnails.

The Output panel after clicking on a few thumbnailsThe Output panel after clicking on a few thumbnailsThe Output panel after clicking on a few thumbnails

Step 37: Detail Image

Now let's turn our attention to the detail image area. As a reminder, the goal here is to load an image, display an attribution, and navigate to a URL when the image is clicked. Simple enough, but let's make sure we approach it with an object-orientation.

Make a new text file and save it as in classes/ui/detail/. Here's are code:

Now, most of this depends on the fact that we'll be passing in a specific movie clip to the constructor. The code controlling this bit of the UI is operating on the assumption that it has a certain Sprite associated with it, in the property called _container. Namely, it expects there to be a TextField somewhere inside the Sprite called "attributionField".

Here's a quick walkthrough of the class: after the imports and boilerplate, we have four properties. The _container is the aforementioned Sprite that is passed in. This will be a Sprite on the stage of the FLA, and we'll get to this again in the next step. The _attributionField is a reference to the TextField inside of the _container. It will just be a convenience to be able to refer to it through a variable rather than always through getChildByName. _loader is a Loader object we create to load the images, and _link is just the destination of the click.

The constructor does most of the heavy lifting. It sets up most of the properties, and sets up the entire Sprite as a button. To round the class out, we have the onClick method that is the CLICK handler, and simply navigates to the URL currently stored in _link. And then there's setImage; this takes information we need to display the DetailImage as a unique image and does the appropriate things with the information: it loads the image source in the Loader, it sets the text to the attribution, and it stores the link for later use in the onClick method.

Again, this isn't a very complex class. We can try to keep things simpler by creating lots of smaller, focused objects.

There's nothing to test yet; save that for the next step when we set up the DetailImage.

Step 38: Display a Detail Image

In order to display a detail image, we'll want to first create a DetailImage object in our document class. So, in ImageViewer, add the highlighted code:

The big thing we do is call the setImage method on our DetailImage instance, and we should get some payoff. Go ahead and test the project, and see if clicking a Thumbnail results in something happening in DetailImage.

Getting an image in the detail areaGetting an image in the detail areaGetting an image in the detail area

Notice that the four lines added are all about the DetailImage. We expanded the functionality of our program by adding one object, and by making changes to one other class that uses that object. All of the other classes (Thumbnail, ImageChangeEvent, etc) have not needed any updates to bring this significant change about. Notice also how our composition hierarchy has grown. Not only do we have our lineage of composition with the thumbnails, but now we have a whole separate branch involving the DetailImage.

The Composition Hierarchy so farThe Composition Hierarchy so farThe Composition Hierarchy so far

And with that in mind, we now add the final piece to this puzzle, the pagination system.

Step 39: Pagination Buttons

Our pagination UI will consist of the two buttons, the left and right arrow buttons in the FLA. Create yet another new text file, saved as PaginationButton in classes/ui/pagination/. Here is the code:

The main point of this class, as a representation of a button, is to hold a Sprite (composition) and provide button-ish functionality to that Sprite. Most of this should be fairly obvious, but we also add the ability to enable and disable the button. This gives the object an easy method for, as you can guess, enabling or disabling the button. The functionality is encapsulated, and is the responsibility of the PaginationButton object. Notice that we are not only managing the interactivity (with _clip.mouseEnabled = true/false;) but also affecting the appearance, to make a disabled button look disabled (with _clip.alpha = 0.5;). Again, this is encapsulation; a simple call to disable disables the button, however the PaginationButton object actually accomplishes that task.

This probably seems like a fairly complete class, but let's add an extra layer.

Step 40: Adding Pagination

We have a class for the individual buttons, but we'd like to group those together into a single entity. We'll create a Pagination class, which will compose the two PaginationButton objects.

Create the last new text file of this project (yay!), call it, and save it in classes/ui/pagination/. Add this code:

This really isn't doing much, other than acting as a place to compose two PaginationButton objects. We might call this object a manager, pretty much existing to aggregate and control a collection of other objects. It does, however, let us treat that collection of objects as a single unit, while the PaginationButton class is a single unit already.

However, a little wrench thrown into our system is how to handle what happens when a button is clicked. Here's what's going to happen: first, we listen for the CLICK events on our two buttons. Then we dispatch a new event in response. The event will an ImageChangeEvent, but the type will not be INDEX_CHANGE but instead a new type. We'll then use the value property to indicate in which direction we need to move from the current image index. We'll need to modify the ImageChangeEvent class, and that happens in the next step.

You'll also notice the updateButtons method. This will give us a way to update the state of the collection of buttons. You can think of this as, again, encapsulation of a more complex task into a less complex command.

Before we get to the ImageChangeEvent, you might be wondering why go to the trouble of creating this class at all. Or possibly why pass in the two Sprites and not create the two PaginationButtons and pass those in. Well, to be honest, the reason is that this is how I chose to do it.

In my opinion (and please keep in mind that a lot of this is just my opinion, laid out in tutorial form), the idea of the pagination system deserves it's own object. When I see individual elements, like the individual pagination buttons, I see those as objects. And if those individual objects can be grouped, even if just conceptually, then that group is also represented by an object. It's not unlike the Thumbnail and ThumbnailCollection classes; the collection is represented by a group.

It's not the only way to accomplish our goal, but it's a way that I've found tends to work well. If you want to try different tactics, by all means, do so. One of the best ways to learn Object-Oriented Programming is to try out ideas, and then see what did and didn't work. Of course, for this tutorial, you'll be best served by following along closely.

Step 41: Update the ImageChangeEvent

Let's make the updates to the the ImageChangeEvent class that we need. This really amounts to just adding the new INDEX_CHANGE type (highlighted below).

That's more or less it. If you haven't noticed, we'll use that value property as something more generic; it's expected that, if it's an INDEX_CHANGE event, then value will contain the destination index value. However, if it's an INDEX_OFFSET event, then value will represent the offset value (that is, how far off from the current index value we're supposed to travel).

Here's our composition hierarchy as of now:

The Composition Hierarchy so farThe Composition Hierarchy so farThe Composition Hierarchy so far

One more step, and we can see the fruits of this labor.

Step 42: Create the Pagination System

We have the classes, we have the Movie Clips on the stage, we just need to turn the ignition. In ImageViewer, we need to create create a Pagination instance and listen for – and handle – its events. Add the following code:

By this point, the additions shouldn't be too shocking. First, in line 10, we import the Pagination class and then create a property for it at line 18. On lines 33 an3420 we create the instance and listen for the INDEX_OFFSET event. The most substantial change is the addition of the onImageOffset method, which is very similar to the onImageChange method, only it needs to pre-calculate the index from the offset value of the ImageChangeEvent and the current index. It also does some bounds checking, to make sure we haven't incremented past the end, or decremented past the beginning. Otherwise, it still calls updateImage with that new index.

Now, the eagle-eyed among you will have noticed the usage of _imageData.length, which isn't currently a valid property on the DataLoader class. You're right; we need to add another data accessor method for DataLoader:

This just provides a nice and clean read-only property that queries the XML data for the number of nodes that exist. With this getter method in place, go ahead and run the movie. You'll be able to click on the arrow buttons now. Note that you can't swing past the beginning or end. This is by design; you could alter the logic to make it loop around to the other end, but for the purposes of this tutorial I want to have hard limits, for purposes of the next step.

Step 43: Keep the UI in Sync

Our last task to make sure the UI updates itself whenever there is any change to the display. For instance, a click on a Thumbnail currently displays the appropriate image. However, it should also draw a selection around the clicked Thumbnail, and also disable the next or previous button, if appropriate. Accordingly, a click on one of the PaginationButtons should not only display the image, but also disable or enable the PaginationButton as appropriate as well as draw the selection around the matching Thumbnail.

You may notice that this is an instance where changes from one part of the UI affect a different part of the UI (aside from the more direct effect of changing the detail image). The temptation may be to have, say, the Thumbnail draw it's own selection when clicked, or the PaginationButtons to enable or disable themselves when clicked. However, if the Thumbnail drew its own selection, then how would me manage the selection when we changed the image from the PaginationButtons? It would be possible, but it would mean we'd be selecting the Thumbnails from two different locations.

Instead, we'll let the hierarchy of composition work for us and keep this UI logic in one place: in ImageViewer. We're already managing the changing of the detail image from there, so why not also manage other UI updates there as well?

Update the updateImage method like so:

All we're doing is expanding the work involved when it's time to update the image, keeping the UI in sync.

Now we need to write the selectThumbnail method on ThumbnailCollection. First, we'll need a property to store the currently selected Thumbnail object:

And then we can write the selectThumbnail method that will use that property:

The logic in this method just makes sure the previously stored Thumbnail (if there is one; there won't the first time we call this method) gets deselected, while the new one gets selected. This technique goes all the way back to the very first AS3 101 tutorial, on variables, if you want a deeper discussion.

Back to the present. It might feel a little strange to have the Thumbnail CLICK dispatch up through the ThumbnailCollection object, then on to the ImageViewer object, which then calls a method on the ThumbnailCollection object, which then calls a method on the Thumbnail object. Why not just have each Thumbnail activate its own selection? Two reasons.

First, we should ideally go back out to our manager class (ThumbnailCollection) for the logic, because not only do we want to select the clicked Thumbnail, but we also want to de-select the previously selected Thumbnail. As it is, each Thumbnail is pretty autonomous, and that's the way we want it (encapsulation), and no individual Thumbnail object knows about any other Thumbnail object. So, we need the ThumbnailCollection to perform the deselection, at the least, and it makes sense to perform the selection from the same location.

Second, as already mentioned, we want to be able to update the selection from other parts of the UI. And it's just a whole lot more manageable if we keep the logic to perform a selection in one place. Therefore, it makes sense for that one place to the "lowest common denominator" of the UI; the ImageViewer class. In our hierarchy, separate branches don't know much about each other. For instance, the pagination branch doesn't know about the thumbs branch, and so a change from the pagination that affects the thumbs would naturally be handle at the place where the branches join, i.e., the ImageViewer class.

Enough theory; go ahead and try out the movie already! The UI should stay in sync; clicking on a thumbnail affects the state of the pagination, and vice versa. Nice going!

The UI working in sync.  Note the last thumbnail is selected, and the next button is disabled.The UI working in sync.  Note the last thumbnail is selected, and the next button is disabled.The UI working in sync.  Note the last thumbnail is selected, and the next button is disabled.

Step 44: A Final Task

One last thing, that's probably bugging you if you have an eye for the useful. We should really bring up an initial image in the DetailImage view. Should be easy enough...but where should implement the change?

We could feed the DetailImage class an initial image to load. This would certainly display the image, but it wouldn't keep the UI updated. The PaginationButtons wouldn't know to disable the previous button (assuming the image is the first image), and we wouldn't get a selection on the Thumbnail.

We could have the ThumbnailCollection automatically dispatch an ImageChangeEvent.INDEX_CHANGE event once it's populated with data, automatically filling in "0" for the value. This would solve the problem of keeping the UI in sync. But is it really the ThumbnailCollection's job to worry about that? I said before that if it works, then it's technically not wrong, but is it the best solution? In my opinion, doing this puts the responsibility in the wrong place. It's not up to the ThumbnailCollection to have an initial selection. We'd really be doing it there for UI syncing purposes, which isn't what the ThumbnailCollection object is about.

Why not just update the UI the same way we've established: have ImageView call updateImage. This way, it's the responsibility of the main document class to decide which image to display initially, and when, or if at all. It's also extremely simple to do so at this point. Just add one line to the onImageDataLoad method (highlighted below):

Run the movie now, and not only do we have a nicely set up initial state, but we've also explored, one more time, the idea of responsibilities and encapsulation.

Step 45: For Further Experimentation

We could take this concept of keeping the UI in sync a bit further, and if you would like the practice I would recommend you do so. However, this tutorial is already longer than average and I must leave you with this challenge:

Make a "slideshow" button that also controls the image display. When clicked, it enters "play slideshow" mode, which starts up a Timer and consequently redispatches the TimerEvents as ImageChangeEvents, of the INDEX_OFFSET type. The slideshow should then pause when clicked again. In addition, it should automatically pause when you click on any of the thumbnails or the pagination (the assumption being that the user is interested in now resuming control of the images, and the auto-advance of the slideshow is no longer wanted).

At its core, this problem is just another branch of the hierarchy of composition, and the same model of dispatching events back the ImageViewer object will do the trick. The implementation details are, of course, going to be different, but the general idea is the same.

Hopefully this conceptual discussion also brought to light an advantage of Object-Oriented Programming: when objects are properly "sealed" and not dependent on each other (that is, encapsulated), adding features becomes almost trivial. You don't have to hunt through a pasta salad of code finding the right things to update. Just add some new objects, and get them instantiated in the right place (yes, that's an over-simplification. I'm trying to sell you on OOP, right?).

Now, as I said, we don't have time to implement this slideshow feature here, and so I encourage you to try your hand at doing it yourself. I did, however, include a version of the project in the download package that has this finished already. If you need a hint, or are just too lazy to do it yourself but want to see it in action, take a look at the image_viewer_slideshow project.


In this tutorial we looked at building a simple project by utilizing Object-Oriented techniques. The project itself was (hopefully) something you could probably handle on your own without objects, but our goal was to demonstrate how a project can be constructed out of several objects and not just all lumped into one script.

The hierarchy of composition is something that takes some getting used to, but with practice it becomes rather natural, and you'll be able to start a new project by looking at the desired result and breaking it down into that hierarchy.

If you're new to Object-Oriented Programming, then all of this will probably be quite dizzying. Please don't be discouraged. It will take quite a bit of practice to get comfortable with OOP. The only way to get better, though, is to actually do it. You will probably write some pretty badly managed classes your first couple times out. That's OK; just learn what you can from the experience, and figure out what you would do differently if you had it to do again. I think it took me about a year from when I first learned OOP to get to a point where I actually felt like I didn't need to be ashamed of the code I had written. Just stick with it. Practice makes perfect, as they say.

With that said, I'm glad you've hung in there so far! This tutorial series isn't even over yet, but this particular installment was rather leviathan in nature. Thanks for reading, and we'll get into some even more advanced Object-Oriented techniques in the next article.

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.