Friday, April 2, 2010

Unable to access children...

Trying to set a label of a loaded flash (that is public), Works fine if I call a timer and wait for 2 seconds. Then I tried to do it the ''right'' way and it fails. (see example code) Aparnently the loaded flash (type FlashLoader) has a child (type SystemManager) with numChildren set to -1 when called from the event.

Tried both init and complete event but no workie.
http://livedocs.adobe.com/flex/3/langref/mx/controls/SWFLoader.html#summaryTable Event
init: Dispatched when the properties and methods of a loaded SWF file are accessible.
complete: Dispatched when content loading is complete.

Here is the example code:
the swf to be loaded only has a label with id innerLabel and is named inner.swf

%26lt;?xml version=''1.0'' encoding=''utf-8'' ?%26gt;
%26lt;mx:Application xmlns:mx=''http://www.adobe.com/2006/mxml'' creationComplete=''init();''%26gt;
%26lt;mx:Script%26gt;
%26lt;![CDATA[
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.events.TimerEvent;
import flash.utils.Timer;
import mx.controls.Label;
import flash.utils.*;
import mx.controls.SWFLoader;
import mx.core.Container;
import mx.core.FlexLoader;
import mx.managers.SystemManager;

public var errors:Array = new Array();
?public var ld:SWFLoader = new SWFLoader();
public function init():void {
?ld.load(''inner.swf'');
?ld.addEventListener(Event.COMPLETE, setInnerLabel);
?this.addChild(ld);
?
?// COMMENT OUT THE ADDEVENTLISTENER AND UNCOMMENT THE NEXT 3 LINES AND IT WORKS
?// LIKE A CHARM
?//var t:Timer = new Timer(2000, 1);
?//t.addEventListener(TimerEvent.TIMER, setInnerLabel);
?//t.start();
var doneTimer:Timer = new Timer(3000, 1);
doneTimer.addEventListener(TimerEvent.TIMER, checkDone);
doneTimer.start();
}
public function checkDone(o:Object):void {
if (this.errors.length != 0) {
trace(this.errors.join(''\n''));
}else {
trace(''Completed without errors'');
}
}

?public function setInnerLabel(o:Object):void {
?var l:Label = getObjectsByProperty(this, ''id'', ''innerLabel'', null)[0] as Label;
?if (l == null) {
?errors.push(''Unexpected innerLabel did not return label......'');
?} else { l.text = ''getbyid OK''; }
?}
?//{ region functions for getElementById
?public function getObjectsByProperty(startFrom:DisplayObjectContainer,
?propertyName:String,
?propertyvalue:String,
?arrRet:Array=null):Array {
?if (arrRet == null) {
?arrRet = new Array();
?}
?// in case you pass a null
?if (startFrom == null) {
?return arrRet;
?}
?if (startFrom.hasOwnProperty(propertyName)) {
?if (startFrom[propertyName] == propertyvalue) {
?arrRet.push(startFrom);
?}
?}
?//trace(''HOW MANY CHILDREN: '' + new String(startFrom.numChildren));
?traceElem(startFrom);
?for (var i:int = 0; i %26lt; startFrom.numChildren; i++) {
?traceElem(startFrom.getChildAt(i));
?if (startFrom.getChildAt(i) is DisplayObjectContainer) {
?var tmpArr:Array = getObjectsByProperty(startFrom.getChildAt(i) as DisplayObjectContainer,
?propertyName, propertyvalue, arrRet);
?arrRet = tmpArr;
?}else {
?trace(''THE FOLLOWING WAS NOT A DISPLAYOBJECTCONTAINTER:'');
?traceElem(startFrom.getChildAt(i));
?}
?}
?return arrRet;
?}
?
?private function traceElem(elem:Object):void {
?var x:XML = describeType(elem);
?var s:String = ''Got an element of type: '' + x.attribute(''name'').toString();
?for (var i:int = 0; i %26lt; x.child(''method'').length(); i++) {
?if (x.child(''method'')[i].attribute('name').toString() == ''getChildAt'') {
?s = s + ''\n\t\t\tSupports getChildAt'';
?}
?}
?for (i = 0; i %26lt; x.child(''accessor'').length(); i++) {
?if (x.child(''accessor'')[i].attribute('name').toString() == ''numChildren'') {
?s = s + ''\n\t\t\tSupports numChildren'';
?s = s + ''\n\t\t\tChildren: '' + new String((elem.numChildren));
?}
?}
?trace(s);
?}
?//} endregion
]]%26gt;
%26lt;/mx:Script%26gt;
%26lt;/mx:Application%26gt;

Unable to access children...

Standard practice is to listen for the APPLICATION_COMPLETE event from SWFLoader.content once you get the COMPLETE event. When loading a Flex SWF the events really mean:

Init: Enough of the properties and methods of SystemManager are available to put up the preloader but we're waiting for the rest of the SWF before we create any other children including the main app:

Complete: All bytes of the SWF are downloaded, but we are waiting for the frame event before switching to frame 2 and creating the main app.

So, your code would look more like:

ld.addEventListener(Event.COMPLETE, completeHandler);

function completeHandler(event:Event):void

{

ld.content.addEventListener(FlexEvent.APPLICATION_ COMPLETE, setInnerLabel);

}

Alex Harui

Flex SDK Developer

Adobe Systems Inc.

Blog: http://blogs.adobe.com/aharui

Unable to access children...

Thanks, that is it. Have to check it later but I am sure it works.

This would be more a problem for the consumer of my classes than for me but it would be nice if I can help them out.

  • blush
  • No comments:

    Post a Comment