Passing extra argument to an event.COMPLETE listener function
I am adding some tabs to the stage, and each tab loads its own swf file. I have an array tracking the tabs, but need to know when each swf is loaded which tab it belongs to. I'm not sure of the best way to do it.
Tabs are controlled by XML, so there can be any number of tabs -
for (var j = 0; j < _xmlTabs.length(); j++) {
arrTabs[j] = new Tab(j,_xmlTabs[j].attri开发者_如何学Cbute("icon"),_xmlTabs[j]);
addChildAt(arrTabs[j],0);
mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));
mLoader = new Loader();
addChild(mLoader);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTabComplete);
mLoader.load(mRequest);
}
Ok, so lets say there are 5 tabs being loaded, and each one loads its own swf, and "onTabComplete()" gets fired 5 times. Even though each tab is created in order, onTabComplete may not fire in order because the swfs will be different sizes. So how do I associate each tab with the onTabComplete function? Each instantiated Tab item does have a "position" property which can be retrieved.
Extend the Loader class with a custom class, add the new property you want, and then you can reference it as
e.currentTarget.loader.newproperty
in the event handler
AS3 supports anonymous functions as well, so you could do something like this:
addChild(mLoader);
var handleComplete:Function = function(event:Event):void {
onTabComplete(event, j);
};
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete);
mLoader.load(mRequest);
Modified version of thehiatus's answer that I think should get around the j value being shared issue:
function buildHandleComplete(i:int):Function {
return function(event:Event):void {
onTabComplete(event, i);
};
}
for (var j:int = 0; j < _xmlTabs.length(); j++) {
arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
addChildAt(arrTabs[j],0);
mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));
mLoader = new Loader();
addChild(mLoader);
var handleComplete:Function = buildHandleComplete(j);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete);
mLoader.load(mRequest);
}
By using a function that takes the index value as an argument and builds the listener using that you end up with each closure referencing a different variable, instead of them all referencing the j value.
Why not identify them with their respective URLs?
private function onTabComplete(e:Event):void
{
var loaderInfo:LoaderInfo = LoaderInfo(e.target);
trace(loaderInfo.url);
//btw, loaderInfo.content is the tab that you are looking for.
var url:String = loaderInfo.url;
var tabIndex:Number;
for(var j:Number = 0; j < _xmlTabs.length(); j++)
{
if(url == String(_xmlTabs[j].attribute("swf")))
{
tabIndex = j;
break;
}
}
//tabIndex now contains the required "j"
}
EDIT: In case tabs load same swfs (same urls).
Even if this works, you should consider redesigning.
private function onTabComplete(e:Event):void
{
int total:Number = _xmlTabs.length();
var loader:Loader = LoaderInfo(e.target).loader;
var p:DisplayObjectContainer = loader.parent;
if(p.numChildren != total * 2)
{
trace("other children present : this method won't work");
return;
}
var ldrIndex:Number = p.getChildIndex(loader);
var tabIndex:Number = ldrIndex - total;
var matchingTab:Tab = arrTabs[tabIndex];
//enjoy
}
in my experience you would want to set up a loading queue. loading them one at a time will prevent a few potential errors
private var j:int = 0; // currentTab number
private function loadNextTab():void{
arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
addChildAt(arrTabs[j],0);
mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));
mLoader = new Loader();
addChild(mLoader);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTabComplete);
mLoader.load(mRequest);
}
private function onTabComplete():void{
_xmlTabs[j].attribute("position")
//Anything else you want to do to instantiate the object.
currentTab++
loadNextTab();
}
that doesn't answer your question about passing variables,
it does answer what i suggest you do with the code.
Passing variables:
one common method is "Custom Events"
http://flexcomps.wordpress.com/2008/04/29/dispatching-custom-events-in-as3-with-data-or-object/
create a class that extends Event, then add a data object to the Event. Any data you want to include would then need to be sent to the data object.
some other methods include:
anonymous functions as thehiatus explains
//errors inside anonymous functions are harder to debug.
and nested functions
private function startLoad():void{
var j:int = 0;
//load request calling LoadComplete
funciton LoadComplete(e:Event){
//has access to j
//errors inside nested functions are harder to debug.
}
}
but these methods would not work well with your for loop either. the data you want to access is reachable by index, but your for-loop blows through the index before any of them finish loading. A load queue is the way to go for your current question.
good luck.
here is a quick solution ! you can use a Dictionary object.
var d:Dictionary=new Dictionary;
for (var j = 0; j < _xmlTabs.length(); j++) {
arrTabs[j] = new Tab(j,_xmlTabs[j].attribute("icon"),_xmlTabs[j]);
addChildAt(arrTabs[j],0);
mRequest = new URLRequest(_xmlTabs[j].attribute("swf"));
mLoader = new Loader();
//associate a tab with his loader...
d[mLoader]=arrTabs[j];
//
addChild(mLoader);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTabComplete);
mLoader.load(mRequest);
}
//
function onTabComplete(e:Event):void{
trace("your tab TAB >",d[e.target]);
}
Associate each loader with the tab (tab has a link to loader), then use the .target member from the event to get the loader that completed and then loop over your tabs to find the tab that has that loader associated.
You'd probably have to create your own tab class if you haven't already to give it a properly to hold the reference to the loader.
I did a similar example of this loading external resources and tracking their progress.
I'm not familiar with the Tab class, but does Tab extend a DisplayObjectContainer? you might be able to just do:
arrTabs[j].addChild(mLoader);
and then in the event handler call:
e.currentTarget.parent.position
to get at the indicator of which tab loaded, then handle appropriately perhaps?
精彩评论