Ticker Component


After customizing the Flash MX components, Lisa sent me an email about the Ticker component that is part of Flash UI Components Set 2.

graphics/01fig13a.gif

graphics/01fig13b.gif

Custom Ticker Component

Based on Lisa's email, we'll create a custom Ticker component for the jukebox. The component will be somewhat simple, since all that's needed is to scroll text. The first step in creating the custom Ticker component is to create a MovieClip that contains two visual elements: a TextField named lbl_txt and the background colors (Figure I-3.5).

Figure I-3.5. UI of the custom Ticker component.

graphics/01fig14.gif

Next, we'll add the following ActionScript to the MovieClip object that will contain all of the methods for the Ticker:

 #initclip  functionTickerClass(){  this.init(); } Object.registerClass("TickerSymbol",TickerClass); TickerClass.prototype=newMovieClip(); TickerClass.prototype.init=function(){  //Setthelabelifitwasspecifiedinthecomponentparameters.  if(this.label!=null){   this.setLabel(this.label);  } } TickerClass.prototype.setLabel=function(lbl){  //Cleartheinterval,"scroll_int",ifitexists.  if(this.scrolling_int!=null){   clearInterval(this.scrolling_int);  }  //Setthetickerlabel  this.lbl_txt.text=lbl+"";  //IfthelabelissmallerthantheTextFieldwidth  //thenappendittoitselfuntilitislarger. if(this.lbl_txt.textWidth<this.lbl_txt._width){  while(this.lbl_txt.textWidth<this.lbl_txt._width){    this.lbl_txt.text+=this.lbl_txt.text;   }  }  //Callthefunction,"scrollText",tobeginscrolling.  this.scrolling_int=setInterval(this,"scrollText",200); } TickerClass.prototype.scrollText=function(){  //Removethefirstcharacterandappendittotheend.  this.lbl_txt.text=this.lbl_txt.text.slice(1)+  this.lbl_txt.text.charAt(0); } #endinitclip 

The first and last lines, #initclip and #endinitclip, are both new statements used to define and register components with Flash MX. These statements allow developers to initialize components before the ActionScript starting on frame 1. Without these statements it would not be possible to immediately call a method of a MovieClip. For the jukebox, we'll want to set the scrolling text using the setLabel() method when the jukebox initializes.

The next line, beginning with functionTickerClass(), creates a TickerClass class or object for the Ticker component. Next, Object.registerClass("TickerSymbol", TickerClass); simply associates the class that was just created, TickerClass, with the current symbol's linkage ID. The linkage ID can be created from the Library by right-clicking the current MovieClip and opening the Linkage Properties dialog box (Figure I-3.6).

Figure I-3.6. Linkage Properties dialog box.

graphics/01fig15.gif

The next line, TickerClass.prototype=newMovieClip();, says to make the TickerClass an instance of a MovieClip. This is done because the symbol in the library, TickerSymbol, is assigned to the same object type that is specified by TickerClass. The next three blocks of ActionScript are the method definitions of the Ticker. Init() initializes the Ticker and calls the method to set the scrolling text, setLabel(). The value of label is derived from the component parameters that can be set from either the Property inspector or the Component Parameter panel. The next method, setLabel(), appends the label to itself until it is wider than the width of the TextField. This is so the text fills the entire scrollable area. The final method, scrollText(), is called every second using setInterval(). This method removes the first character of the string and adds it to the end.

With the ActionScript, UI elements and linkage created for the Ticker, the final step in creating a component is to go back to the Library and right-click the current MovieClip and open the Component Definition dialog box (Figure I-3.7).

Figure I-3.7. The Component Definition dialog box.

graphics/01fig16.gif

In the Component Definition dialog box, there will be only one parameter, label, which will set the scrolling text. The label can also be set by calling the setLabel() method and passing the label variable as a parameter. To use the custom Ticker component, we'll drag it from the library and name it ticker_mc. Next, in the init() function, the Ticker label will be set with ticker_mc.setLabel("Pleaseselectasongtoplay.");. As a new song is played, the same method will be called, passing the song info as a parameter.

Sound Object

The next step in the init() function is to create a Sound object, song, and define the events that will be used in the jukebox to play MP3s.

NOTE The Sound object comes with a number of methods and events. Using the Sound object, it is possible to attach sounds from the Library or load MP3s directly into the Flash 6 Player. Methods include the ability to stop and start a sound, in addition to setting the volume and pan. Unfortunately, it is not possible to pause a sound. The Sound object has two events, onLoad and onSoundComplete. The onLoad event occurs when a sound is finished loading, and onSoundComplete occurs when a sound is finished playing.

 song=newSound();  song.onLoad=function(){this.start()}; song.onSoundComplete=next; 

The first line creates a new Sound object. The next line tells the jukebox what to do when the MP3 finishes loading. In this case, it says to start playing the song. The final line says what to do when the song finishes playing, which is to call the function next(), which will be created later. This same function will be used in the control that plays the next song.

SharedObject

In order to store user settings, we'll be using SharedObjects, which are new with Flash MX. SharedObjects are similar to cookies and in this case will be used to store a user's jukebox settings. When Flash MX was first released, my team had vaguely heard of SharedObjects, but since they weren't documented very well, we didn't pay much attention to them. Since the release, more information has been made available on Macromedia.com and other sites, so I asked Lisa to take a closer look at SharedObjects.

graphics/01fig16a.gif

graphics/01fig16b.gif

graphics/01fig16c.gif

SharedObject Creation

The SharedObject for the jukebox will be created and defined in the init() function. To create a SharedObject for the jukebox, we'll use the following ActionScript:

 settings_so=SharedObject.getLocal("jukeboxSettings");  

For the SharedObject that is used in the jukebox, we'll want to store five user settings: volume, pan (left/right balance), current song index, current song total time, and the playlist data. To modify these settings, we'll create five methods for the SharedObject that will modify these properties. Additionally, six methods will be created that will be used to retrieve the same properties that were set in the first five methods. The methods that will be created are commonly referred to as getter/setter methods because they get and set properties of the object. To assign a method to the Shared Object created above, we'll define anonymous functions for the settings_so SharedObject.

Functions are a set of ActionScript statements that perform a task. Functions can be defined on one location of a movie and then invoked from another. An example of a function could be to display an error message when the user has done something wrong. The function would be written out with the following:

 functionshowError(){     //Displaytheerrormessagedialogbox. errorDialogBox._visible=true; } 

If this function were sitting on the main timeline, the developer could now call this function from anywhere in the movie by entering _root.showError();. Furthermore, if the developer wanted to make a change to the function, the change would be reflected in all calls to the function. Functions can also receive parameters, which the function uses. In this case, the function could be rewritten with the following:

 functionshowError(msg){     //Setvalueoferrormessage.errorDialogBox.lbl.text=msg;    //Displaytheerrormessagedialogbox. errorDialogBox._visible=true; } 

The function now receives a parameter that is used to set the error message in the dialog box. The parameter passed to the function, msg, is used as a placeholder that the function uses when it sets the error message. To call this function, the developer would now use _root.showError ("Pleaseenterausername.");, which passes the error message to the function. Functions can also be used to set methods of objects. An example of a method of an object is the removeMovieClip() method of the MovieClip object. For the jukebox, we'll assign methods to the settings_soSharedObject. For advanced users, an object can be created in ActionScript, and then methods can be assigned to the object's prototype property, which subsequently makes the methods available to all instances of the object.

To create a method that sets the volume for the settings_so Shared Object, we'll use the following:

 settings_so.setVolume=function(volume){   this.data.volume=volume; } 

This method will be called when a user releases the volume control. This method receives one parameter, volume, and assigns this value to the volume property of settings_so. SharedObjects are assigned properties by using the data property and then adding the property name next. Before the data property is the keyword this. The this keyword references settings_so since this is the object to which the method belongs.

NOTE The keyword this was new with Flash 5 and can be used to reference an object or MovieClip. This can be thought of as referencing oneself. If this is used within a function or on a timeline, then this refers to the current timeline. If this is used as part of an object's method, then this refers to the current object. In the case of the settings_so, this refers to settings_so.

On the corresponding get methods, we'll use something similar to return the volume property:

 settings_so.getVolume=function(){   returnthis.data.volume; } 

This method returns the volume for the settings_so SharedObject. It will be called when the jukebox loads to set the volume to the previous setting.

Using a method to set and get the volume rather than changing the volume property directly is a better practice because it provides a separation between the inner workings of an object and its relationship to other objects. For example, if a property name of an object is changed, then it can be done safely, since calling a method does not directly call the property name; rather, it calls the method of the object, which in turn retrieves the property name.

The next six methods that will be defined for the settings_so Shared Object will be similar to the setVolume() and getVolume() methods.

The first two methods will be used to set and get the volume:

 jukeboxSettings.setPan=function(pan){   this.data.pan=pan; } jukeboxSettings.getPan=function(){  returnthis.data.pan; } 

The next two methods will be used to get and set the song index of the current song (the index is the position in the playlist):

 jukeboxSettings.setSongIndex=function(index){   this.data.songIndex=index; } jukeboxSettings.getSongIndex=function(){  returnthis.data.songIndex; } 

The last two methods will be used to get and set the length of the current song:

 jukeboxSettings.setSongTime=function(songTime){   this.data.songTime=songTime; } jukeboxSettings.getSongTime=function(){  returnthis.data.songTime; } 

To set the playlist, the following method will be used:

 jukeboxSettings.setPlayList=function(){   varplayListLabels=newArray(),playListIDs=newArray();  varitem;  //Loopthrough"playList_lb"andputthevaluesintoanarray. for(vari=0;i<playList_lb.getLength();i++){   item=playList_lb.getItemAt(i);   playListLabels[i]=item.label;   playListIDs[i]=item.data;  }  //PutthearrayvaluesintotheSharedObject.  this.data.playListLabels=playListLabels;  this.data.playListIDs=playListIDs; } 

Using this method, settings_so will get synchronized with the playList_lb ListBox that stores that playlist. Each line in the ListBox component is an item, and each item has two parameters label, which is visible in the ListBox, and data, which is an optional parameter that associates a value to the item. In the case of the playList_lb ListBox, the label of each item will consist of the artist name and then the label. The data value will be the song ID that will be used to load the MP3 and JPEG when a song is played. To store the songs from the playlist in settings_so we'll use two Arrays: one for the labels and the other for the song IDs. Arrays are helpful for storing this data because they have a length property that will be used to loop through the Array and get the data of each element. When the jukebox is initially loaded, the playlist data in the SharedObject will be used to populate the playlist. To populate the playlist in settings_so, two Arrays, playListLabels and playListData, will be created by looping through the items in the playlist. Next, the Arrays will be assigned to settings_so.

The methods to retrieve the playlist information from settings_so will be similar to the other get methods:

 jukeboxSettings.getPlayListLabels=function(){   returnthis.data.playListLabels; } jukeboxSettings.getPlayListIDs=function(){  returnthis.data.playListIDs; } 

Like the other get methods, these methods will be called when the jukebox is loaded, and the data will be used to populate the playlist.

Volume and Pan Control

Similar to the custom Ticker component will be the volume and pan (left/right balance) controls (Figure I-3.8). Although these two controls will not be components, they will be created similarly so that their methods can be called from the init() function.

Figure I-3.8. Volume and pan controls.

graphics/01fig17.gif

The following will define and register the VolumeClass class. This class contains one method that will set the level of the slider and set the volume of the Sound object, song, used to play MP3s:

 #initclip  functionVolumeClass(){}; Object.registerClass("VolumeSymbol",VolumeClass); VolumeClass.prototype=newMovieClip(); VolumeClass.prototype.setVolume=function(vol){  _parent.song.setVolume(vol);  this.control_mc._x=vol*1.4; } #endinitclip 

The following will define and register the PanClass class. This class contains one method that will set the level of the slider and set the pan of the Sound object, song, used to play MP3s:

 #initclip  functionPanClass(){}; Object.registerClass("PanSymbol",PanClass); PanClass.prototype=newMovieClip(); PanClass.prototype.setPan=function(pan){  _parent.song.setPan(pan);  this.control_mc._x=pan*.7; } #endinitclip 

In the init() function, the following ActionScript will check for volume and pan settings:

 //Ifthereisasavedvolumethensetit.  if(settings_so.getVolume()!=null){  volume_mc.setVolume(settings_so.getVolume()); } //Ifthereisasavedpanlevelthensetit. if(settings_so.getPan()!=null){  pan_mc.setPan(settings_so.getPan()); } 

If the value is null, then the controls will default to their current levels, and the song Sound object will be set to its default settings of volume equal to 100 and pan equal to 0, which sets the left/right balance evenly.

Populate Playlist

After creating a SharedObject and defining its methods, the jukebox will next check for null values for each particular property, and if the value is not null, then the settings will be reflected in the jukebox (Figure I-3.9).

Figure I-3.9. Playlist.

graphics/01fig18.gif

If there are songs from settings_so to add to the playlist, then populatePlayList_lb will be called:

 functionpopulatePlayList_lb(){   varplayListLabels=settings_so.getPlayListLabels();  varplayListIDs=settings_so.getPlayListIDs(); for(vari=0;i<playListLabels.length;i++){   playList_lb.addItem(playListLabels[i],playListIDs[i]);  } } 

This function populates the ListBox, playList_lb. The first step of the function is to get the labels and IDs in settings_so. The values returned are Arrays, since that is how the data is stored in the setPlayList() function. Next, a loop is created that will loop through the Array and add items to playList_lb by using the addItem() method.

Search Pane

In order to add a song to the playlist, a user has to be able to search for a song (Figure I-3.10).

Figure I-3.10. Search pane.

graphics/01fig19.gif

Searching for a song will be done in the search pane, search_mc. Based on the UI, the search pane will consist of a TextField where the user can enter a search string, a Search PushButton, and the searchResults_lb ListBox. The PushButton and ListBox are both components. When a user makes a search request, the database will be queried for both the artist and the song. The query is initiated when the user clicks the Search PushButton. When the search is made, the jukebox calls a CFC that will query the database and then return the search results, which will populate searchResults_lb.

Animation with ActionScript

To initiate a search, a user must first click the GetMusic PushButton. This PushButton will then call a function, moveTo(), of the search pane that will reposition it so that it slides into view from the bottom. The search pane will slide into place by moving halfway to the target y-coordinate every 50 milliseconds. Taking half the distance will cause the search pane to appear to decelerate into place.

 //Thisfunctionsetsthe"Y"coordtargetvalue.It  //thencallsthesetY()functionevery50milliseconds. functionmoveTo(targetY){  this.targetY=targetY;  move_int=setInterval(setY,50); } //Thisfunctionhalvesthecurrent"Y"coorduntilit //reachesthetarget"Y"value.Ifitiswithinone //pixelthencleartheinterval. functionsetY(){  newY=(_y+targetY)/2; if(Math.abs(newY-targetY)<1){   _y=targetY;   clearInterval(move_int);  }else{   _y=newY;  } } 

The moveTo() function takes one parameter. The parameter is the target y-coordinate of search_mc. This function will be used twice in the movie: to open the search pane and to close it. When the search pane is initially opened, the target y-coordinate is above the current y-coordinate and vice versa when it is closed. The first statement of the function sets the target y-coordinate that will be used when calculating the new y-coordinate. The next statement calls another function every 50 milliseconds that will calculate the position of the search pane. This is done using the setInterval() method. The setInterval() method is new with Flash MX and allows one to create intervals based on time rather than frame rate.

Once the y-coordinate is within 1 pixel of the target y-coordinate, the coordinate of the search pane is set to the target y, and the interval is cleared.

Search Query

With the search pane in place, a user will next be able to submit a query to the database. To do this, the user will first enter a search string in the TextField, txtSearch_txt, and then click the PushButton, search_pb. When the user clicks search_pb, the function, search(), will be called.

 functionsearch(){   //Setthestatustext.  status_txt.text="\nRetrievingsearchresults...";  //RemovetheitemsfromtheListBox,"searchResults_lb",  //ifthereareany. if(searchResults_lb.getLength() >1){   searchResults_lb.removeAll();  }  //Makeaconnectiontotheserverandaserviceobjectif  //itdoesnotexist.  if(cfServer==null){   //EstablishaconnectiontotheColdFusionserver. varurl="http://localhost:8500/flashservices/gateway";  NetServices.setDefaultGatewayUrl(url);   //ConnecttoFlashRemoting.   cfServer=NetServices.createGatewayConnection();   //Requesttheservice,"song",andcreateaserviceobject,   //"song_svc"   song_svc= cfServer.getService("morningRecords.services.song",this);  }  //Callthemethod,"getSongs",andpassitthesearchstring  //fromtheTextField,"txtSearch".  song_svc.getSongs(search_txt.text); } 

The first step of this function is to set the status of the search in the TextField, status_txt. The status_txt TextField is located over the searchResults_lb ListBox and will be updated as the search progresses. After the status is set, all of the items from searchResults_lb are removed. Next, a check is made to see if there is a connection to the server available. If it is null, then a connection is made. In order to connect to the server, the Flash Remoting ActionScript Classes must be included in the movie. For any Flash MX applications that require Flash Remoting, these should be included in the first frame of the movie.

Flash Remoting ActionScript Classes

Before doing anything else in our movie, we will write an ActionScript statement to include the necessary ActionScript classes used for Flash Remoting. These classes can also be thought of as objects like a MovieClip that has methods such as duplicateMovieClip() that will be used in connecting to Flash Remoting. Including the classes can be done using the following:

 #include"NetServices.as"  #include"NetDebug.as" 

The first line includes the ActionScript classes for Flash Remoting. The second line is for development use and includes the NetDebug class. To connect to an application server running Flash Remoting and call a service object, the NetServices and NetConnection ActionScript classes are used. Using the NetServices object, we create a connection to the Flash Remoting gateway:

 cfServer=NetServices.createGatewayConnection();  

With the connection, we can next create a service object that will be used to access the functions exposed through Flash Remoting:

 song_svc= cfServer.getService("morningRecords.services.song",this);  

For the jukebox, we'll create the song_svc service object that references the ColdFusion component (CFC), song. The CFC will be located in the services folder, which is inside of the morningRecords folder. The morningRecords folder is located off of the CFMX wwwroot. To access the CFC the path, morningRecords.services.song, is used. After a service object has been created, we can call any of the methods that exist in the service in this case, getSongs()willbecalled.

song.cfc

The song CFC handles the data transactions pertaining to songs. For the jukebox there is only one function, but if other functions were needed pertaining to songs, they would be added to this CFC.

 <cfcomponentname="song">  <cffunctionname="getSongs"access="remote"returnType= "query"> <cfargumentname="search_txt"type="string"required="no"> <cfqueryname="rsGetSongs"datasource="#APPLICATION.dsn#">    SELECTSONGS.SONG_ID,SONGS.SONG_NAME,ARTISTS.ARTIST_ NAMEFROMSONGS,ARTISTSWHERE(SONG_NAMELIKE'%#search_txt#%' ORARTIST_NAMELIKE'%#search_txt#%')ANDSONGS.ARTIST_ID= ARTISTS.ARTIST_IDORDERBYARTISTS.ARTIST_NAME,SONGS.SONG_ID </cfquery> <cfreturnrsGetSongs> </cffunction> </cfcomponent> 

The getSongs function receives one argument, search_txt. This argument is passed to the CFC when the method is called with the following:

 song_svc.getSongs(search_txt.text);  

This value passed is the value that the user entered in the search_txt TextField. This argument is then referenced in the SQL query as #seatch_txt# surrounded by "%" to denote wildcards. When the query is complete, the result is passed back to the jukebox with <cfreturn rsGetSongs>.

Database Setup

The database for the jukebox is fairly simple. It contains two tables: ARTISTS and SONGS. The ARTISTS table contains the Morning Records artist IDs and names. The SONGS table contains the song IDs, names, and artist IDs. To connect to the database, a data source name (DSN), morningRecords, is created in the ColdFusion MX Administrator (Figure I-3.11).

Figure I-3.11. Adding a DSN in the ColdFusion MX Administrator.

graphics/01fig20.gif

A reference to the DSN is then stored in the application.cfm file, which is used to store application variables and settings. The DSN variable can then be referenced in other ColdFusion files using #APPLICATION.dsn#. For the jukebox application this variable is used only once, but in much larger applications it is more efficient to store application variables and settings in one place.

Returned Query

When the results are returned to the jukebox, the method of the service object that was originally called appended with _Result is called; in this case, getSongs_Result() is automatically called. If any errors are returned from Flash Remoting, the Player will look for the method name that was originally called appended with _Status; in this case, if there was an error, getSongs_Status() would be automatically called if it exists. Using Flash Remoting, the result set is returned as an array in result.items. The number of records corresponds to the length of the array. Each column of the database that is returned by the query can be accessed using the column name. For example, song.cfc returns two columns, SONG_ID and SONG_NAME. To access the SONG_ID column of the first record of the result set, one would use result.items[0].SONG_ID.

 functiongetSongs_Result(result){   //Setthestatustext.  status_txt.text="";  //Checktoseehowmanyrecordswerereturned.  varresultCount=result.items.length;  if(resultCount==0){   status_txt.text="Nosongsorartistsmatchedyoursearch string.Pleasetryagain."  }else{   varitem;   //Populate"searchResults_lb"withtheresults. for(vari=0;i<resultCount;i++){    item=result.items[i]; searchResults_lb.addItem(item.ARTIST_NAME+":"+ item.SONG_NAME,item.SONG_ID);   }  } } 

The first line of this function is to set the status to nothing. Next a reference is made to the number of items that were returned from the CFC:

 varresultCount=result.items.length;  

If there was nothing returned, then the status is updated. If there were results returned, then the ListBox, searchResults_lb, is populated with results from the query.

 for(vari=0;i<resultCount;i++){   item=result.items[i];  searchResults_lb.addItem(item.ARTIST_NAME+":"+ item.SONG_NAME,item.SONG_ID); } 

The items are added to searchResults_lb by looping through the results and adding items with the addItem() method of the ListBox. The first parameter passed to addItem() is the label, which in this case is the artist name followed by the song name. The second parameter is the data value that corresponds to the song ID.

Adding to the Playlist

After a user has added songs to the search results, the songs can then be added to the playlist. This is done by first selecting songs from the search results and then clicking the PushButton, ADD, which calls the function addSong().

 functionaddSong(){   //Addasong(s)onlyifsomethingwasselected.  varitems=searchResults_lb.getSelectedIndices();  if(items!=null){   items.sort();   //Settheinsertionpointoftheselectedsong(s).   //IftherearemultipleselectionsthensorttheArray   //andaddabovethetopselection.   varaddIndex= _parent.playList_lb.getSelectedIndices().sort()[0]; if((_parent.activeSong >=addIndex)&&(addIndex!=null)){    updateActiveSong=true;   }else{    updateActiveSong=false;   }   //Ifnothingisselectedthenaddittotheend.   if(addIndex==null){    addIndex=_parent.playList_lb.getLength();   }   varaddItem;   //Addtheselectionstotheplaylist. for(vari=0;i<items.length;i++){    addItem=searchResults_lb.getItemAt(items[i]);    _parent.playList_lb.addItemAt(addIndex,addItem.label, addItem.data);    addIndex++;   }   //Update"activeSong"ifthesongswereaddedbeforethesong which   //iscurrentlyplaying."activeSong"isusedtodeterminethe song   //toplaynext.   if(updateActiveSong){    _parent.activeSong+=i;   }   //RefreshtheplaylistintheSharedObjectafter   //addingthesongs.   _parent.settings_so.setPlayList();  } } 

The first line of the function creates a reference to the Array of selected items in the search results. If something was selected, then the Array is sorted with items.sort();. This will sort the Array in ascending order so that the items are added to the playlist in the order in which they appear in the search results. After the Array has been sorted, addIndex is set to designate the insertion point of the selected songs into the playlist so that when songs are added to the playlist, they are added above the topmost selection. This is determined by getting the selected items of the playlist, sorting the Array, and then getting the index of the first item:

 varaddIndex=_parent.playList_lb.getSelectedIndices(). sort()[0]  

After the insertion point is created, a flag is set to determine if activeSong should be updated. The activeSong variable keeps track of the index of the currently playing song. If nothing is selected in the playlist, then the songs will be added to the end of the playlist by setting addIndex=_parent.playList_lb.getLength();

With addIndex set, the songs can be added to the playlist by looping through the selected songs in the search results. This is done using the addItemAt() method of the ListBox:

 for(vari=0;i<items.length;i++){   addItem=searchResults_lb.getItemAt(items[i]);  _parent.playList_lb.addItemAt(addIndex,addItem.label, addItem.data);  addIndex++; } 

The addItemAt() method takes three parameters. The first one specifies where to add the item, and the next two, like the addItem() method, specify the item label and data. The addIndex variable is used to set the index in which the item should be added. Since items will be added after one another in the playlist, addIndex will be incremented each time through the loop. After the loop is completed, activeSong will be incremented by the number of songs added if songs were added before it. The final step is to update the settings_so SharedObject that stores the user settings:

 _parent.settings_so.setPlayList();  

Play Control IconButtons

The jukebox will have four controls to control the playback of songs: previous, next, play, and stop. Each of these controls will use the IconButton of the Flash UI Components Set 2. Working with these buttons is similar to working with the PushButton component. The main difference is the ability to use an icon in the button. As with other components, changes to the component properties can be made in the Component Parameters panel (Figure I-3.12).

Figure I-3.12. Component Parameters for the Stop IconButton.

graphics/01fig21.gif

To use an icon with the IconButton component, the linkage ID of the symbol to use for the icon is entered in the Icon field. As with other components, handlers are assigned to the component by creating a function and then specifying it in the Change Handler field. A Change Handler tells the component what to do when an event occurs. In this case, when the user clicks the Stop button, stopSong() will be called.

 functionstopSong(){   song.stop();  clearInterval(elapsedTime_int);  elapsedTime_txt.text="00:00"; } 

The stopSong() function has three statements. The first one stops the song Sound object from playing. The next statement clears the interval that updates the elapsed time of a song. This interval is set when a song starts playing. After clearing that interval, the third statement resets the elapsed-time display.

The previous() and next() functions, which correspond to the previous and next IconButtons, are similar to each other:

 functionprevious(){  if((activeSong-1) >=0){   //Playtheprevioussongaftertheonethatwaslast   //selectedtoplay.   playList_lb.setSelectedIndex(activeSong-1);   playSong();  } } functionnext(){ if((activeSong+1)<playList_lb.getLength()){   //Playthenextsongaftertheonethatwaslast   //selectedtoplay.   playList_lb.setSelectedIndex(activeSong+1);   playSong();  } } 

Both functions set the selected item in the playlist based on the activeSong variable, which keeps track of the index of the current song playing and then calls the playSong() function. The playSong() function is based on the current selection in the playlist.

 functionplaySong(){   //Assigntheselectedsongsto"items".  varitems=playList_lb.getSelectedIndices();  //Playasongonlyifsomethingwasselected.  if(items!=null){   //Resetthesongtimes.   elapsedTime_txt.text="";   totalTime_txt.text="";   //Sorttheselectedsongsiftherearemorethantwo,   //sothetopselectionisfirst. if(items.length >1){    items.sort();   }   //Settheactivesong.Thisvariableisusedwhenthesong   //endssothenextsongplaysincaseadifferentonewas   //selectedduringplaybutwasnotplayed.   activeSong=items[0];   //Setthetopselectionastheonlyselection.   playList_lb.setSelectedIndex(activeSong);   //Getthedataofthefirstitem.   varitem=playList_lb.getSelectedItem();   //Loadtheimage.   loadJpg(item.data);   //Setthescrollingtickerlabel.   ticker_mc.setLabel(item.label);   //LoadtheMP3.   loadSong(item.data);   //SetthesongintheSharedObject.   settings_so.setSongIndex(activeSong);  } } 

Since multiple selections can be made in the playlist, the getSelected Indices() method of the ListBox is called on the first line of the function. This method will return an Array of the indexes of the selected items in the ListBox. Since we'll be using this value a few times in the function, a reference to the Array will be created.

 varitems=playList_lb.getSelectedIndices();  

Next, a statement checks to see if anything was selected. If something was selected, the items Array is sorted using items.sort(). This is because getSelectedIndices() returns an Array in the order in which the items were selected. When a song is selected to play, we want the song selected at the top of the list to play. Next, the first position of the Array is assigned to activeSong:

 activeSong=items[0];  

TheactiveSong variable will keep track of the current song playing and will be used when adding and removing songs to and from the playlist, or using the next and previous buttons. After activeSong has been set, the playlist is updated so that only the top item is selected. The remaining steps load the JPG, set the ticker label, load the song, and update the SharedObject.

LoadingJPEG

When a song is selected to play, a new JPEG is loaded that represents the album cover. This is accomplished by calling the loadJpg() function:

 functionloadJpg(id){   imageStatus_txt.text="Loadingimage...";  //Unloadthecurrentimagesostatusisvisible.  jpgLoader.unloadMovie();  //Loadtheimage.  jpgLoader.loadMovie("images/"+id+".jpg"); } 

This function takes one parameter, id, which corresponds to the name of the JPEG and the data value from the playlist. The first line sets the status message of the loading JPEG. The next line unloads the current image so that the status is visible. The final line of the function loads the JPEG from the file system. The JPEGs are located in the images folder, which is on the same level as the jukebox SWF.

Loading MP3

While the JPEG is loading, the MP3 is also loaded by calling the loadSong() function.

 functionloadSong(id){   song.loadSound("music/"+id+".mp3",false);  totalTime_txt.text="of\n"+Math.round(song.getBytesTotal()*.001);  kbLoaded_int=setInterval(getKbLoaded,10); } functiongetKbLoaded(){ if(song.getBytesLoaded() >=song.getBytesTotal()){   //Resetthetimes.   elapsedTime_txt.text="00:00";   totalTime_txt.text="of\n00:00";   clearInterval(kbLoaded_int);   //Callthemethod,whichsetsthethetotalsongtime,using   //"setInterval".Thisisduetoproblemswithretrievingthe   //totalsonglength.   totalTime_int=setInterval(getTotalTime,25);   //Gettheelapsedtimeeverysecond.   elapsedTime_int=setInterval(getElapsedTime,1000);  }else{   elapsedTime_txt.text=Math.round(song.getBytesLoaded()* .001);  } } functiongetTotalTime(){  if(song.duration!=0){   totalTime_txt.text="of\n"+formatTime(song.duration);   settings_so.setSongTime(totalTime_txt.text);   clearInterval(totalTime_int);  } } functiongetElapsedTime(){  elapsedTime_txt.text=formatTime(song.position); } 

As with the loadJpg() function, this song uses the same ID to load the MP3. Since the MP3s will be larger in file size than the JPEGs, this function provides the user with a status report as to how much has loaded. After the statement to load the song is made, the song's total size is displayed, and an interval is called that will calculate the kilobytes loaded until the entire song has been loaded. After the song is completely loaded, the total time and elapsed time are calculated. The total time is retrieved within an interval because there are problems with Sound.duration returning a zero, so an interval has been created that will check the total time until a nonzero duration is retrieved. The getElapsedTime() function is called every second to display the total elapsed time.

Removing ListBox Items

The final functionality that will be created is the ability to remove songs from the playlist. This function will be called when a user clicks the remove button. One of the benefits of the ListBox component is the ability to select multiple items. For the jukebox, this feature will be turned on so that if a user selects multiple items, they will all be removed.

 functionremove(){   //Removeasong(s)onlyifsomethingwasselected.  varitems=playList_lb.getSelectedIndices();  if(items!=null){   //Sorttheselecteditems.ThisisbecausetheArray   //ofselecteditemsisdeterminedbyselectionorder.   items.sort();   //Removetherestoftheitemsbuttakeintoaccountthe   //itemsremovedalreadysothecorrectitemisremoved. for(vari=0;i<items.length;i++){    //Checktoseeifthesongbeingremovedislessthan    //"activeSong". if(activeSong >(items[i]-i)){   activeSong-=1; if(activeSong<0){   activeSong=0; }     //SetthesongintheSharedObject.     settings_so.setSongIndex(activeSong);    }    playList_lb.removeItemAt(items[i]-i);    playList_lb.setSelectedIndex(activeSong);   }   //Iftherearenomoreitemsin"playList_lb"thensetthe   //selectionbacktonull.   if(playList_lb.getLength()==0){    playList_lb.setSelectedIndices(null);    settings_so.setSongIndex(null);   }   settings_so.setPlayList();  } } 

Similarly to the playSong() function, this function will first create a reference to the Array of selected items. If something was selected, the Array is then sorted so that activeSong is updated correctly and the correct item is removed. To remove the songs and update activeSong, the selected items are looped and removed, taking into account the items that already have been removed. If the activeSong is removed, then the song above it in the playlist is selected as the activeSong. After the songs are removed and if the playlist is empty, then the selection of the playlist is set to null, as is the settings_so song index. The final step in the function is to update settings_so with the latest playlist. This is done by calling the method defined earlier that loops through the playlist and updates the SharedObject.

With the development phase of the application complete it's time to show the jukebox to Angie and other members of her team. We'll use their feedback to see what additional changes have to be made before the jukebox goes live for customers.



Reality Macromedia ColdFusion MX. Macromedia Flash MX Integration
Reality Macromedia ColdFusion MX: Macromedia Flash MX Integration
ISBN: 0321125150
EAN: 2147483647
Year: 2002
Pages: 114

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net