Get Adobe Flash player

Pagination in Flash - Part 2

A lot of the pieces of this tutorial will look familiar to those of you who read the first pagination tutorial. There are enough differences however, that it is worth going through all the code once again. One of the big differences is that we are using buttons to move through the pages. Before we get started on the code itself, let's take a look at one way you can make your buttons in Flash.

Make Your Own Buttons

First thing is to make your buttons in PhotoShop, or whatever your favorite graphics program is. Once you have the buttons ready, open up your fla and go to File -> Import -> Import To Library, and bring your buttons in. You can download a zip file called pagDemoButtonSkins.zip that contains the png files and the original psd file if you want to see the set up. It was made using PhotoShop CS3.

Make sure you have already dragged an instance of Button from the Components to your library. With that in there, you will also have a folder called Component Assests. Open that and then open the Button Skins folder. There you will find all the skins that are associated with your button. These are all the default skins. Right click on the Button_upSkin and choose Duplicate. In the panel that comes up name your new skin Button_upSkinPN. Now check the Export for ActionScript checkbox and click OK.

You will see the new skin appear in the Button Skins folder. Right click on your new skin and choose Edit. In the timeline for this moveiclip add a new layer at the top above the highlight layer and drag your button_up instance from the library to the stage. Align it to the top and the left. Now delete the 3 layers under it on the timeline so that the only thing left is your new layer.

Now do the same steps with the Button_overSkin, Button_downSkin, Button_disabledSkin, Button_selectedDownSkin, Button_selectedOverSkin,and Button_selectedUpSkin, naming them Button_overSkinPN, Button_downSkinPN, etc.

And here's how we use our new buttons:

myButton.setStyle("upSkin", Button_upSkinPN);
myButton.setStyle("overSkin", Button_overSkinPN);
myButton.setStyle("downSkin", Button_downSkinPN);
myButton.setStyle("selectedUpSkin", Button_selectedUpSkinPN);
myButton.setStyle("selectedOverSkin", Button_selectedOverSkinPN);
myButton.setStyle("selectedDownSkin", Button_selectedDownSkinPN);
myButton.setStyle("disabledSkin", Button_disabledSkinPN);

We will be using the Toggle property on our buttons, so I have also created the skins for the _selected options. In all cases for the selected skins - up, down, over - we use our button_down skin.

The Demo Movie

The movie below feeds on 2 XML files containing the song data. I wanted the demo to make use of dynamically gathered data, and XML seemed to be the best choice under the circumstances. How you get your data is unimportant to the pagination concept, it's what we do with it afterwards that counts. For this tutorial I needed a lot more data in order to effectively show off the page numbers, so I cheated and just copied and pasted the entries in the rockMusicPN.xml file until I had enough. I mention this in case you are wondering why the songs repeat.

The Demo Files

Now, download the nextPrevPN.zip file and let's dig into the code and see how this works.

The files included in the nextPrevPN.zip are PagDemoPN.as and pagDemoPN.fla. The easyMusicPN.xml and rockMusicPN.xml files hold the data that we will be reading into the movie. And, you'll find the NextPrevPN.as class and the CustomEvent.as class files inside the ca.xty.myUtils folder system.

The XML files are very simple examples, and so I hope they don't need any detailed explanation, because, once again, I'm not providing any. There are quite a few good tutorials about using Flash and XML and, to do the subject justice, I'd need to make this tutorial even longer than it is. Besides, you will most likely have much more complex data to work with in a real world situation, and consequently a much more complex XML file. What I'll mainly be dealing with is gathering the data from the XML file and packaging it up in a way that's easy for us to use.

There is also a text file called backToTop.txt. This is a tiny bit of JavaScript that we will use together with the ExternalInterface class provided by Flash.

And, as mentioned above there is a zip file with the PhotoShop psd file containing the artwork for the button skins used in this demo.

As usual, the fla is empty, except for a button in the library, our new skins, and the fact that the Document Class property is set to PageDemoPN.

What's not included in the zip file this time out is an fla for CS3 - but don't despair! Creating one is dead easy. Make a new fla in CS3 with a width of 500 and a height of 600. Drag a Button component into the library and set your Document Class to read PagDemoPN - remember, no .as extension, just the class name. Make sure you have all the files included in the zip in the same folder as your CS3 fla and you're good to go.

Future tutorials will likely be done in Flash CS5 now that I have finally moved up to that version. If you are doing a lot of custom class work, do yourself a favor and move up too. The custom class introspection is worth it all by itself.

So let's jump into the pagDemo.as file already!

PagDemoPN.as

In order to make this work we need to have some data to make a results list from. In this example we are using two XML files to provide that data. Open up the pagDemoPN.fla and it's Document Class, PagDemoPN.as. You'll notice in the pageDemoPN.fla that I haven't lied to you, and there is nothing on the the stage and the only thing in the library is an instance of the Button component. Have a peek in the Component Assets -> ButtonSkins folder and you will find all of our custom skins. Under the Document Class setting we have declared PagDemoPN - no .as, just the class name - as the Document Class. Let's switch over to that file.

Starting from the top, we import the necessary classes.

package{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.net.*;
    import flash.text.*;
    import fl.controls.Button;
    import flash.external.ExternalInterface;
    import ca.xty.myUtils.NextPrevPN;
    import ca.xty.myUtils.CustomEvent;

The two custom classes we need are found in the ca.xty.myUtils folder and they are NextPrevPN.as and CustomEvent.as. We will discuss these, and the ExternalInterface class, in more detail as we go along. Next we set up our variables.

public class PagDemoPN extends MovieClip{
 
    //XML variables
    private var dataLoader:URLLoader = new URLLoader();
    private var xmlData:XML = new XML();
    private var musicInfoArray:Array;
 
    //On screen assets
    private var musicBut1:Button;
    private var musicBut2:Button;
    private var goBtn:Button;
    private var t1:TextField;
    private var ip1:TextField;
 
    //Data display variables
    private var barArray:Array;
    private var titleArray:Array;
    private var composerArray:Array;
    private var descArray:Array;
    private var songLengthArray:Array;
    private var cartArray:Array;
    private var titleHead:TextField;
    private var timeHead:TextField;
    private var composerHead:TextField;
    private var descHead:TextField;
    private var buyHead:TextField;
    private var buyArray:Array;
 
    //Pagination variables
    private var npT:NextPrevPN;
    private var npB:NextPrevPN;
    private var useNP:Boolean = false;
    private var maxNum:int = 7;
    private var numSongs:int;
    private var itemStart:int = 0;
    private var itemsToShow:int;
    private var pageStart:int = 1;
    private var numVisible:int;
    private var nBtn:Boolean;
    private var pBtn:Boolean;
    private var fBtn:Boolean;
    private var lBtn:Boolean;
    private var firstPage:Boolean = true;
    private var resultsTop:Sprite;
    private var resultsHolder:Sprite;
    private var newPage:int;
    private var newPageBut:int;
    private var numPages:int = 8;

    // TextFormats
    private var titleFormat:TextFormat;
    private var titleFormatC:TextFormat;
 
    //X and Y positions
    private var xPos:int;
    private var yPos:int;

First up, we get our XML variables ready. We declare a URLLoader to get the XML files off the server. Then we have a an XML object, and finally an array called musicInfoArray which is an array that we will be dumping all the results from the XML files in.

Our On Screen assets include the buttons to grab the music and another button which allows us to set the number of results we want to show at one time. The textfields are a label field and an input field to enter in the number of desired results.

The Data Display variables are the things we will be using to show the results. These consist of arrays and textfields.

The Pagination variables are the things will will be using to keep track of where we are and what is happening within the pagination process. The first two, npT and npB are variables to represent instances of our class NextPrevPN and stand for NextPrevPN Top and NextPrevPN Bottom. The useNP is a Boolean that will tell us whether or not we need to use an instance of the NextPrevPN class by determining the number of results and balancing that against the next variable maxNum. So, if we have 5 results and our maxNum is 10 then we do not need to use the NextPrevPN instance. If the opposite is true, then we need it. Then we have a bunch of integer values to pass information back and forth to the NextPrevPN class, a few more Booleans to let us know where we stand, and finally a couple of Sprites that we will be using as containers to hold our results. Using containers like this will provide us an easy way to remove the results in a single step. Notice that the two variables, maxNum and numPages have their values set. maxNum represents the maximum number of items to show per page, and numPages represents the maximum number of page number buttons to show at one time. You can alter these variables to meet your needs. The other two variables that are preset are itemStart and pageStart. The first time out itemStart will always be 0 and pageStart will always be 1. itemStart is 0 because this variable wants to get the first item in an array, which starts with a 0 index, and pageStart is set to 1 so that it makes sense to your visitors who think that the first page is 1, not 0.

Next up is our constructor.

public function PagDemoPN(){
 
    //Set up the TextFormats
    titleFormat = new TextFormat();
    titleFormat.color = 0x000000;
    titleFormat.font = "verdana";
    titleFormat.size = 10;
    titleFormat.align = "left";
 
    titleFormatC = new TextFormat();
    titleFormatC.color = 0x000000;
    titleFormatC.font = "verdana";
    titleFormatC.size = 10;
    titleFormatC.align = "center";
 
    //Give our XML Loader a Complete event listener
    dataLoader.addEventListener(Event.COMPLETE, loadData);
 
    //Put our assets on stage
    musicBut1 = new Button();
    musicBut1.x = 20;
    musicBut1.y = 10;
    musicBut1.width = 120;
    musicBut1.height = 20;
    musicBut1.label = "Rock Music (84)";
    musicBut1.addEventListener(MouseEvent.CLICK, musicButHandler);
    addChild(musicBut1);
 
    musicBut2 = new Button();
    musicBut2.x = 150;
    musicBut2.y = 10;
    musicBut2.width = 120;
    musicBut2.height = 20;
    musicBut2.label = "Easy Listening (4)";
    musicBut2.addEventListener(MouseEvent.CLICK, musicButHandler);
    addChild(musicBut2);
 
    t1 = new TextField();
    t1.x = 280;
    t1.y = 10;
    t1.width = 90;
    t1.height = 20;
    t1.text = "Items to Show:";
    t1.setTextFormat(titleFormat);
    addChild(t1);
 
    ip1 = new TextField();
    ip1.x = 375;
    ip1.y = 10;
    ip1.width = 30;
    ip1.height = 20;
    ip1.type = "input";
    ip1.border = true;
    ip1.defaultTextFormat = titleFormatC;
    ip1.text = String(maxNum);
    addChild(ip1);
 
    goBtn = new Button();
    goBtn.x = 410;
    goBtn.y = 10;
    goBtn.width = 30;
    goBtn.height = 20;
    goBtn.label = "Go";
    goBtn.addEventListener(MouseEvent.CLICK, goHandler);
    addChild(goBtn);
 
}

First we give our TextFormats some properties. They are nearly identical, with the only difference being the text align property.

Next we add a COMPLETE Event Listener to our XML dataLoader and direct it at a function called loadData.

Now we stick our stage assets in place. These are the buttons that will allow you to choose between Rock Music and Easy Listening. The numbers in parentheses represent the number of songs available in each category. I have taken a short cut here. In a real application, you would be counting the number of incoming results and appending that number to the button label. The last three assets go to make up our number-of-results-to-see-at-once feature. A title field with a label of Items to Show, an input field to gather the answer and the goBtn to make it happen. Our goBtn has an event listener for a CLICK event and takes you to a function called goHandler. Using the page number buttons this time out, you will also be affecting the total number of page buttons when you change the number of items to show. For instance, Rock Music has 84 items. The default is 7 items per page so we have a total of 12 page buttons. If you were to change the number of items to show to say 12, then you would have a maximum of 7 pages.

Now we'll look at the function that handles the user changing the number of items to show per page.

private function goHandler(e:MouseEvent):void{
    //As long as the input field is not empty, we change the variable maxNum to the contents of the ip1 textfield
    if(ip1.text != ""){
        maxNum = Number(ip1.text);
    }
    //If the boolean firstPage is not true we remove the data already displayed on stage and reset our variables to start over
    if(!firstPage){
        removeChild(resultsTop);
        removeChild(resultsHolder);
        if(useNP){
            removeChild(npB);
        useNP = false;
        }
        firstPage = true;
        itemStart = 0;
    }
    //Check to see if numSongs is greater than the new maxNum and then buildResults again using the the new maxNum
    if(numSongs > maxNum){
        useNP = true;
    }else if(numSongs <= maxNum){
        itemsToShow = numSongs;
    }
    // Now build the results
    buildResults();
}

The first thing we check is whether or not or input field, ip1, has anything in it. As long as it is not equal to nothing, we set the variable maxNum to whatever is in the ip1 field. Next we need to determine if the boolean firstPage is true or not. If it is not true, then that means we already have something on stage that we need to get rid of before proceeding. So we remove our two Sprites, resultsTop and resultsHolder. Now we check the useNP variable to see whether we are using an instance of our NextPrevPN on the bottom. If it's true, then we remove that instance as well. Once everything is off stage, we reset the firstPage variable to true, and our itemStart to 0. Finally we check to see if numSongs is greater than the new maxNum, and then buildResults() again using the the new maxNum.

Next we'll look at our music button handler.

private function musicButHandler(e:MouseEvent):void{
    switch(e.currentTarget.label){
        case "Rock Music (84)":
            dataLoader.load(new URLRequest("rockMusicPN.xml"));
            break;
        case "Easy Listening (4)":
            dataLoader.load(new URLRequest("easyMusicPN.xml"));
            break;
    }
}

We use a switch statement, with the label data as the criteria to tell which button has been clicked, and then load the appropriate XML file. You'll remember that we already set up our listener for the dataLoader and declared it's function to be loadData.

private function loadData(e:Event):void {
    try{
        xmlData = new XML(e.target.data);
        parseData(xmlData);
    } catch (e:TypeError){
        trace("Unable to load XML");
    }
}

Here we assign the incoming data property to our XML object called xmlData, and then send that data to a function called parseData.

private function parseData(dataInput:XML):void {
    musicInfoArray = new Array();
    var infoList:XMLList = dataInput.song.composer;
    numSongs = infoList.length();
    for(var i:int = 0; i < numSongs; i++){
        var comp:String = dataInput.song[i].composer;
        var track:String = dataInput.song[i].title;
        var tl:String = dataInput.song[i].track_length;
        var d:String = dataInput.song[i].description;
        var musicObj:Object = {Composer:comp, Track:track, TrackLength:tl, Desc:d};
        musicInfoArray.push(musicObj);
    }
    //If the boolean firstPage is not true, then we remove the current data displayed and reset our variables to start over
    if(!firstPage){
        removeChild(resultsTop);
        removeChild(resultsHolder);
        if(useNP){
            removeChild(npB);
            useNP = false;
        }
        firstPage = true;
        itemStart = 0;
    }
    //If numSongs is greater then the maxNum to be shown, then we will need our next/prev feature so set the boolean useNP to true
    //and then buildResults
    if(numSongs > maxNum){
        useNP = true;
    }else if(numSongs <= maxNum){
        itemsToShow = numSongs;
    }
    // Now we build the results
    buildResults();
}

We set our musicInfoArray to empty by declaring a new instance of it. This array is going to hold an array of objects, with each object in the array containing all the information we need about one song. Next we declare a temporary variable called infoList that will hold our XMLList data, and we set it to point to the composer block in our XML file. We determine our numSongs variable by giving it the length value of our infoList. Notice here that the length property is declared as a function - length() - not the length property we normally see when getting the length of an array - someArray.length.

Now we use a for loop to gather all the available data and package it into an object - musicObj -, which we then push into our musicInfoArray. Once we have all the data stored away, we run through our check to see if this is the firstPage or not, and whether the numSongs is greater than the maxNum we set. This is the same routine as in our goHandler function we discussed above. Once we are ready, we buildResults().

Page 2 | Page 3