Creating an XML Grid Image Gallery in Flash (ActionScript 3.0) - part 3
In this part of the tutorial we will make our thumbs load the full version of themselves when a thumb is clicked. We are going to use the Loader Class to load the external image the same way we did with thumbs. However, to attach the code to all the thumbs in one go we will make use of some advanced AS3 Event Handling techniques.
The outline of this section is as follows:
- Registering for the mouse click event.
- Loading the full image when a thumb is clicked.
- Unloading the full image when the full image is clicked.
Registering For the Mouse Click
I wouldn't usually put the registration of an event handler as a step by itself in a tutorial, but this tutorial is a little bit different. We need to execute a unique command from each of our thumbs and that would usually require us to register each thumb on its own to an event listener. However, AS3 event handling system has a unique feature called event propagation. This feature lets a parent object pass an event listener registered to it to an unlimited number of children automatically - saving effort and time as you can register the event with a parent of a group of children instead of registering it with each and every child.
We are going to use this technique for registering the mouse click event to the thumbnails. Instead of registering the event listener to the thumbnails, we will register this event to the parent container_mc. The purpose of our event listener would be to load the full image, so we will call this function callFull(). We are going to register this function with container_mc in the createContainer() function:
function createContainer():void {
container_mc = new MovieClip();
container_mc.x = my_x;
container_mc.y = my_y;
addChild(container_mc);
container_mc.addEventListener(MouseEvent.CLICK, callFull);
}
Loading the Full Image
To actually load the full image we obviously have to define that callFull() listener function. We are going to create an instance of the Loader Class to load our external image. At the bottom of your entire code create this function:
function callFull(e:MouseEvent):void{
var full_loader:Loader = new Loader();
}
We need to pass the URL of the full image to load to this instance of the Loader Class. We have the URL as an attribute in our XML array of images. We need a unique reference from each thumb to tell us what image to load from that XML array. The easiest method for creating this reference is by setting the thumb's name property as its number in the XML list and this way we can know which full image to load. Go back to the callThumbs() function and add this line to save the value of i in each thumb as its name:
function callThumbs():void{
for (var i:Number = 0; i < my_total; i++){
var thumb_url = my_images[i].@THUMB;;
var thumb_loader = new Loader();
thumb_loader.load(new URLRequest(thumb_url));
thumb_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoaded);
thumb_loader.name = i;
thumb_loader.x = (my_thumb_width+10)*x_counter;
thumb_loader.y = (my_thumb_height+10)*y_counter;
if (x_counter+1 < columns){
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
}
}
We now can use this reference to load the full image we need from the XML list the same way we retrieved the thumb url before. We will create a temporary variable and then pass it to the .load() method of the Loader Class to load the image:
function callFull(e:MouseEvent):void{
var full_loader:Loader = new Loader();
var full_url = my_images[e.target.name].@FULL;
full_loader.load(new URLRequest(full_url));
}
The reference to e.target is a reference to the thumbnails clicked. You can use e.currentTarget to refer to the parent MovieClip to which the event was registered.
Our image will not be displayed because we did not add it to the display list. Just like the thumbs we need to display it only after it successfully loads. However, instead of register for the event .COMPLETE this time we will register for the event .INIT which is triggered when the file is downloaded and initialized. When a file is initialized this means that we can retrieve all its properties, and that differs from the completion of the download process, as the file might be downloaded, but still not initialized. We need to access the properties of our image to be able to center it on the stage.
To do all of this we will start off by register for the event .INIT with a function called fullLoaded which will deal with the image once it loads:
function callFull(e:MouseEvent):void{
var full_loader:Loader = new Loader();
var full_url = my_images[e.target.name].@FULL;
full_loader.load(new URLRequest(full_url));
full_loader.contentLoaderInfo.addEventListener(Event.INIT, fullLoaded);
}
Our fullLoaded() listener function has two functions, (1) display the instance of the Loader Class on stage, and (2) center it. Adding the object to the display is pretty simple, you just use the addChild() method. Centering the image on the screen is a little bit trickier, as it will require us to retrieve the width of the stage, the width of the image and then use a little bit of algebra to get it centered. Here is a simple diagram showing how this is supposed to work:
This same technique is used for the height as well. Now we can code it simply by pasting the following at the bottom of our existing code:
function fullLoaded(e:Event):void{
var my_loader:Loader = Loader(e.target.loader);
addChild(my_loader);
my_loader.x = (stage.stageWidth - my_loader.width)/2;
my_loader.y = (stage.stageHeight - my_loader.height)/2;
}
You can test your movie now to see that when you click on a thumb the full version of it shows! (You can't remove it though, and that's what we'll do next!)
So how do we remove this image from the screen? We need to add another event listener to it and simply tell it to unload the image and remove the loader from the display list. First, lets register the event listener. Go to the fullLoaded() listener function and register for the MouseEvent.CLICK:
function fullLoaded(e:Event):void{
var my_loader:Loader = Loader(e.target.loader);
addChild(my_loader);
my_loader.x = (stage.stageWidth - my_loader.width)/2;
my_loader.y = (stage.stageHeight - my_loader.height)/2;
my_loader.addEventListener(MouseEvent.CLICK,removeFull);
}
Our function now has to unload the image by using the unload() method of the Loader Class and then remove the loader completely from the display list using the removeChild() method. This function should go below all of your existing code:
function removeFull(e:MouseEvent):void{
var my_loader:Loader = Loader (e.currentTarget);
my_loader.unload();
removeChild(my_loader);
}
Okay, test your movie and you should see that you can click on a full image to remove it from the screen!
We could do this in the polishing up stage, but I thought I'll deal with it now because it is an obvious annoyance. You may have noticed that you can click on the thumbnails on the edges of the screen and they will show their full images, if you do that and then you click on a full image to remove it, you will see the previous one still behind it. You can also click multiple times on an image to load several copies of itself on top of each other.
The solution to this problem is to remove the event listener when not needed and put it back when needed. The event listener we are talking about is the one attached to the container_mc and which is responsible for loading the full images. We are going to unregister this event when someone clicks on a thumb. Look for the listener function called callFull() and simply let it unregister itself from the container this way:
function callFull(e:MouseEvent):void {
var full_loader:Loader = new Loader();
var full_url = my_images[e.target.name].@FULL;
full_loader.load(new URLRequest(full_url));
full_loader.contentLoaderInfo.addEventListener(Event.INIT, fullLoaded);
container_mc.removeEventListener(MouseEvent.CLICK, callFull);
}
We can register this one again when someone removes the full image. Find the removeFull() listener function and use it to register the callFull() listener function again:
function removeFull(e:MouseEvent):void{
var my_loader:Loader = Loader (e.currentTarget);
my_loader.unload();
removeChild(my_loader);
container_mc.addEventListener(MouseEvent.CLICK, callFull);
}
Great, that should do it perfectly now. Test it locally to see your grid gallery working like a charm!
We have done a lot but we are not finished yet. We still need to do add preloaders, add some tweens, and do some final polishing. We'll do these in order, first moving to the preloaders in the next section.
Summary of this Page
- Registered container_mc with a mouse click event.
- Creating an event listener to call the full image when a thumb is loaded.
- Created an event listener to remove the full image when it is clicked.
In the next section we will create preloaders for all our images.
We are going to add preloaders for both the thumbnails and the full images. For doing this we will be using the ProgressBar component. You should be able to use it with ease if you have read our previous tutorial on using the Loader Class.
- Adding the ProgressBar Component graphical assets to our SWF.
- Adding the ProgressBar to the thumbnails.
- Adding the ProgressBar to the full image.
Adding the ProgressBar Component graphical assets to our SWF
The graphical assets of the ProgressBar components are not by default included in any SWF you make. We need to add it to the library of your FLA in order to be able to use it. Doing this is pretty easy: Open up the Components Panel by going through Window>Components. Now look for the ProgressBar Component under the User Interface category, once you find it drag a copy of the ProgressBar component to the stage... then delete it! If you open your library now (Ctrl+L), you will see that you have the assets needed for the ProgressBar in there.
We have the ProgressBar Component graphical assets available for use in our grid gallery. We will now move back to ActionScript.
Adding the ProgressBar to the Thumbs
Back to the Actions panel, we have the graphical assets ready, but we also need to import the ActionScript Class of the ProgressBar as well to use it in ActionScript. So right at the top of your code, use the import command to import the class we need this way:
import fl.controls.ProgressBar;
Just the same way we created a MovieClip container for our thumbs, we need to create a MovieClip container for our progress bars, we can use the same container we used before, but that will make our preloaders clickable (because the event handler is passed to all the children of that MovieClip). This means that we have to create a separate container. So start off by defining a variable to hold this MovieClip and call it preloaders_mc:
var columns:Number;
var my_x:Number;
var my_y:Number;
var my_thumb_width:Number;
var my_thumb_height:Number;
var my_images:XMLList;
var my_total:Number;
var container_mc:MovieClip;
var preloaders_mc:MovieClip;
var x_counter:Number = 0;
var y_counter:Number = 0;
We will now put an actual MovieClip in the variable and set it up, use the same function we used to create the first container (createContainer()) to create this one as well:
function createContainer():void {
container_mc = new MovieClip();
container_mc.x = my_x;
container_mc.y = my_y;
addChild(container_mc);
container_mc.addEventListener(MouseEvent.CLICK, callFull);
preloaders_mc = new MovieClip();
}
We need this MovieClip to be positioned in the same exact place as the first container, so we will set its x and y properties to the same values that container_mc has:
function createContainer():void {
container_mc = new MovieClip();
container_mc.x = my_x;
container_mc.y = my_y;
addChild(container_mc);
container_mc.addEventListener(MouseEvent.CLICK, callFull);
preloaders_mc = new MovieClip();
preloaders_mc.x = container_mc.x;
preloaders_mc.y = container_mc.y;
}
Now add this MovieClip to the display list to make its content visible when we add them later:
function createContainer():void {
container_mc = new MovieClip();
container_mc.x = my_x;
container_mc.y = my_y;
addChild(container_mc);
container_mc.addEventListener(MouseEvent.CLICK, callFull);
preloaders_mc = new MovieClip();
preloaders_mc.x = container_mc.x;
preloaders_mc.y = container_mc.y;
addChild(preloaders_mc);
}
Our container is now ready, we will now create some instances of the ProgressBar class, set them up, and display them using the callThumbs() function and loop. The code you should add is pretty self explanatory:
function callThumbs():void{
for (var i:Number = 0; i < my_total; i++){
var thumb_url = my_images[i].@THUMB;;
var thumb_loader = new Loader();
thumb_loader.load(new URLRequest(thumb_url));
thumb_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoaded);
thumb_loader.name = i;
thumb_loader.x = (my_thumb_width+10)*x_counter;
thumb_loader.y = (my_thumb_height+10)*y_counter;
if (x_counter+1 < columns){
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
var preloader_pb:ProgressBar = new ProgressBar();
preloader_pb.source = thumb_loader.contentLoaderInfo;
preloader_pb.x = thumb_loader.x;
preloader_pb.y = thumb_loader.y;
preloaders_mc.addChild(preloader_pb);
}
}
The ProgressBar Class is configured by setting its
.source instance property to an instance of the
ContentLoaderInfo class attached to a loader class instance. Please refer to the
Loader Class tutorial for more info on this topic.
You can test your movie now by by pressing Ctrl+Enter and then pressing it again Ctrl+Enter simulate download.
It should work, but you will notice that the preloaders do not go away, and that they look pretty messed up: Wrong width and strange positioning. The trick is to make the ProgressBar just as wide and tall as the thumbs themselves. To do this we can simply set its width and height properties to the thumb width and height properties which have retrieved from the XML file:
function callThumbs():void{
for (var i:Number = 0; i < my_total; i++){
var thumb_url = my_images[i].@THUMB;;
var thumb_loader = new Loader();
thumb_loader.load(new URLRequest(thumb_url));
thumb_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoaded);
thumb_loader.name = i;
thumb_loader.x = (my_thumb_width+10)*x_counter;
thumb_loader.y = (my_thumb_height+10)*y_counter;
if (x_counter+1 < columns){
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
var preloader_pb:ProgressBar = new ProgressBar();
preloader_pb.source = thumb_loader.contentLoaderInfo;
preloader_pb.x = thumb_loader.x;
preloader_pb.y = thumb_loader.y;
preloader_pb.width = my_thumb_width;
preloader_pb.height = my_thumb_height;
preloaders_mc.addChild(preloader_pb);
}
}
Test your move once again, you see that your preloaders look nice, BUT they do not go away once they are done and they cover up the actual thumbs, so they are no good!
Removing these preloaders once they finish their job will require us to register an event listener to check for an event to check that they are done loading and then simply use the removeChild() method to remove them from the display list. Start off by registering for the event listener:
function callThumbs():void{
for (var i:Number = 0; i < my_total; i++){
var thumb_url = my_images[i].@THUMB;;
var thumb_loader = new Loader();
thumb_loader.load(new URLRequest(thumb_url));
thumb_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, thumbLoaded);
thumb_loader.name = i;
thumb_loader.x = (my_thumb_width+10)*x_counter;
thumb_loader.y = (my_thumb_height+10)*y_counter;
if (x_counter+1 < columns){
x_counter++;
} else {
x_counter = 0;
y_counter++;
}
var preloader_pb:ProgressBar = new ProgressBar();
preloader_pb.source = thumb_loader.contentLoaderInfo;
preloader_pb.x = thumb_loader.x;
preloader_pb.y = thumb_loader.y;
preloader_pb.width = my_thumb_width;
preloader_pb.height = my_thumb_height;
preloaders_mc.addChild(preloader_pb);
preloader_pb.addEventListener(Event.COMPLETE, donePb);
}
}
We will now create the listener function called donePb() which will simply have the roll of removing the progress bar from the display list, type this at the bottom of your entire code:
function donePb (e:Event):void{
var my_pb:ProgressBar = ProgressBar(e.target);
preloaders_mc.removeChild(my_pb);
}
Test your movie once again now and simulate download, the progress bars should work fine and then disappear when their job is done!
This does it for the thumb preloaders, we will now work with the full image.
Adding a ProgressBar Preloader for the Full Image
Adding a progress bar preloader for the full image is much easier because it is a single image and because we already have our container now, so all we have to do is just create an instance of the ProgressBar Class, set it up, and center it on stage. Go to your callFull() method and just add the code highlighted below, which is again pretty self explanatory:
function callFull(e:MouseEvent):void {
var full_loader:Loader = new Loader();
var full_url = my_images[e.target.name].@FULL;
full_loader.load(new URLRequest(full_url));
full_loader.contentLoaderInfo.addEventListener(Event.INIT, fullLoaded);
var full_pb:ProgressBar = new ProgressBar();
full_pb.source = full_loader.contentLoaderInfo;
full_pb.x = (stage.stageWidth - full_pb.width)/2;
full_pb.y = (stage.stageHeight - full_pb.height)/2;
preloaders_mc.addChild(full_pb);
container_mc.removeEventListener(MouseEvent.CLICK, callFull);
}
We used the same technique we used before to center the preloader.
You can test your movie now to see that it works, but again the progress bar does not go away. We can register the same event listener we used before here and it will remove the progress bar once it is done loading. Simply register for the same event and there is no need to create the function again because we already have it.
function callFull(e:MouseEvent):void {
var full_loader:Loader = new Loader();
var full_url = my_images[e.target.name].@FULL;
full_loader.load(new URLRequest(full_url));
full_loader.contentLoaderInfo.addEventListener(Event.INIT, fullLoaded);
var full_pb:ProgressBar = new ProgressBar();
full_pb.source = full_loader.contentLoaderInfo;
full_pb.x = (stage.stageWidth - full_pb.width)/2;
full_pb.y = (stage.stageHeight - full_pb.height)/2;
preloaders_mc.addChild(full_pb);
full_pb.addEventListener(Event.COMPLETE, donePb);
container_mc.removeEventListener(MouseEvent.CLICK, callFull);
}
Great, you can test your movie now, simulate download and you will see that you have a perfectly working grid gallery!
Our grid gallery now has all the mechanical functionality we need for it to run online. The next page of the tutorial will teach you how to make it fancier by using the Tween Class to add its fade-in and fade-out animation.
Summary of this Page
- Added the graphical assets of the ProgressBar Component to the library.
- Added a ProgressBar preloader to all the thumbs.
- Added a ProgressBar preloader to the full image.
In the next section we add a fade in and out animation for all of our images.