Passing Informaton for Files with Loader - actionscript-3

I'm using Loader to get the image data out of a ByteArray. The problem is that I need to store that image data with a name (which is known beforehand) once it's passed to the complete handler. Since the operation is asynchronous, I can't be sure which image of multiple will finish loading first, so it seems I need to pass the information with it somehow... I can't seem to find any properties of Loader that pass any vaguely useful information along.
Any recommendations on how I might accomplish this?

Couldn't you just use the Loader.name property?
var ldr:Loader = new Loader();
ldr.name = 'name_of_the_loader';
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderListener);
ldr.loadBytes(aByteArray);
...
function loaderListener(event:Event):void {
trace('name of the completed loader is '+LoaderInfo(event.target).loader.name);
}
Could you provide some code?

private var loaders:Array = [];
private var names:Array = [];
//inside loadImages method
for(i = 0; i < len; i++)
{
var ldr:Loader = new Loader();
//add listeners and call load
loaders.push(ldr)
names.push(name-of-ith-image);
}
private function onLoadComplete(e:Event):void
{
var index:Number = loaders.indexOf(LoaderInfo(e.target).loader);
var requiredName:String = names[index];
trace(requiredName);
}

First solution would be to use a Dictionary to map the Loader instances to names.
Like this:
private var names : Dictionary = new Dictionary();
...
var ldr : Loader = new Loader();
names [ ldr ] = 'someName';
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderListener);
ldr.loadBytes(aByteArray);
...
function loaderListener(event:Event):void {
trace('name of the completed loader is '+ names[ event.target ] );
}
The other solution would be to use a functor, like this:
var ldr : Loader = new Loader();
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, createListener( 'someName' ) );
ldr.loadBytes(aByteArray);
...
function createListener( imgName : String ) : Function {
return function ( event : Event ) : void {
trace('name of the completed loader is '+ imgName );
}
}

loader.contentLoaderInfo.url will be having URL of the loaded image (e.g http://sometURL/image1.jpg).

Related

How to deserialize array collection in flex

I am stuck with an issue:
In which I am serializing an array collection to a file, the array collection has two type of items an Image and a class object.
It serialize successfully, but when I de-serialize it, It simply returns array collection of objects and I found my self unable to convert these objects into Images and the class object(this class is defined by myself and having three members, two images and a shape).
I am adding the code which I used:
[Bindable] var objcnvs:ClassCanvas;
protected function btnSave_clickHandler(event:MouseEvent):void
{
//for saving data serializaion
var arrAllSave:ArrayCollection = new ArrayCollection();
for(var i:int = 0;i<arrAll.length; i++)
{
try
{
var tempCon:ConnectImage = arrAll[i];
arrAllSave.addItem({item:arrAll[i], type:"ConnectImage" });
}
catch(er:Error)
{
var tempImage:Image = arrAll[i];
var objImage:ClassImage = new ClassImage(arrAll[i]);
arrAllSave.addItem({item:objImage, type:"Image" });
}
}
// First, generate your ByteArray from the VO.
var byteArray : ByteArray = new ByteArray();
byteArray.writeObject( arrAll );
// Resolve your file location.
//var file : File = File.applicationStorageDirectory.resolvePath( "testFile.ri" );
var mapName:String = txtMapTitle.text;
var file : File = File.applicationStorageDirectory.resolvePath( 'Saved Maps/'+mapName+'.imm' );
if(file.exists == true)
{
lblWarn.text = "Map already Exists. Please enter Defferent Map Title";
}
else if(mapName == "")
{
lblWarn.text = "Please enter a title for Map.";
}
else
{
var fileStream:FileStream = new FileStream();
// Save the file to the given location.
fileStream.open(file, FileMode.WRITE);
fileStream.writeBytes( byteArray );
fileStream.close();
lblWarn.text = "Map Saved successfully";
}
}
protected function btnLoadMap_clickHandler(event:MouseEvent):void
{
// Execute the file load.
var loadFileName:String = "t1";
var request : URLRequest = new URLRequest ( "app-storage:/"+"Saved Maps/"+loadFileName+".imm" );
var receptor : URLLoader = new URLLoader( request );
// Make sure our content is interpreted as a ByteArray.
receptor.dataFormat = URLLoaderDataFormat.BINARY;
receptor.addEventListener( Event.COMPLETE, fileLoadedHandler );
}
private function fileLoadedHandler ( event : Event ) : void
{
// Retrieve the event target, cast as the URLLoader we just created
var loader : URLLoader = event.target as URLLoader;
// Retrieve the loaded data. We know it's a ByteArray, so let's cast it as well.
var data : ByteArray = loader.data as ByteArray;
// Use the ByteArray.readObject method to reconstruct the object.
var obj : Object = data.readObject();
// Cast the object and assign it to our data container.
var loadArrAll:ArrayCollection = obj as ArrayCollection;
}
The last line
var loadArrAll:ArrayCollection = obj as ArrayCollection;
this is the issue. I get the array collection but it has only a list of objects no images and no my class objects (although these are there but I found no way to convert this array collection in previous form when I serialized it.)
class that needs to be serialized/deserialised needs to implement flash.utils.IExternalizable.
public function readExternal(input:IDataInput):void - here you restore object
public function writeExternal(output:IDataOutput):void - here you saving object
best regards
Zain, you have to declare your classes like that:
package com
{
[RemoteClass]
public class myClass
{
public function myClass()
{
}
}
}

passing parameters to addEventListener AS3

I am struggling passing paramters to function on onComplete event handler.
It seems that my problem is with the event.Complete code..
I just want to load image from a url and transfer parameter.
This is my code:
var imageURLRequest:URLRequest = new URLRequest(pic);
var myImageLoader:Loader = new Loader();
myImageLoader.load(imageURLRequest);
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(evt:Event.COMPLETE){
doIt(evt, "Shift key was down:")
},false,0, true);
function doIt(evt:Event, msg:String) {
var myBitmapData:BitmapData = new BitmapData(myImageLoader.width, myImageLoader.height);
myBitmapData.draw(myImageLoader);
var myBitmap:Bitmap = new Bitmap;
myBitmap.bitmapData = myBitmapData;
}
Thank you very much.
Remove .COMPLETE from the handler inner function so that your listener looks like this:
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(evt:Event)
{
doIt(evt, "Shift key was down:")
} , false, 0, true);
Look at the Loader class as loader, not as DisplayObject even when it is:
var myBitmap:Bitmap;
var contentLoader:Loader = new Loader();
contentLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleComplete);
contentLoader.load(imageURLRequest);
function handleComplete(e:Event):void
{
myBitmap = contentLoader.content as Bitmap;
}
Firstly, as Gio said, remove that .COMPLETE from evt:Event.COMPLETE because it's returning a String instead of the Event the function needs.
Then, instead of setting that last fearsomely unpredictable parameter (useWeakReference) to true in your addEventListener(), I recommend you keep the reference in a variable to use removeEventListener() on it at the right time. A way to do this (while answering your question) is:
var imageURLRequest:URLRequest = new URLRequest(pic);
var myImageLoader:Loader = new Loader();
myImageLoader.load(imageURLRequest);
var functionDoIt:Function = doIt("Shift key was down:");
myImageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, functionDoIt);
function doIt(msg:String):Function {
return function(evt:Event):void {
// Now you can use both "msg" and "evt" here
var myBitmapData:BitmapData = new BitmapData(myImageLoader.width, myImageLoader.height);
myBitmapData.draw(myImageLoader);
var myBitmap:Bitmap = new Bitmap(myBitmapData);
}
}
// Remove the listener when you don't need it anymore:
//myImageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, functionDoIt);
You can understand this solution better by reading this answer.

Problem with progressEvent Listener

for (i=0; i < _xmlContents.img_array.length; i++)
{
_loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadingAction);
//Event.COMPLETE listnere
//error Handler
}
private function onLoadingAction(e:ProgressEvent):void
{
_preLoader = new Preloader();
//addChild(_preLoader);
trace(_loader.name);
}
I want to add preloader for every image in the xml. Now I am getting for last image only.
(consider if xml length is 5, it will trace image4 only)
How can I add that?
It is because you have one _loader object. In every loop step you overwrite this loader with new one so previous image stops loading. You should use new loaders for every image:
for (i=0; i < _xmlContents.img_array.length; i++)
{
// create new loader instance, not use a global one
var _loader:Loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadingAction);
//Event.COMPLETE listnere
//error Handler
_preLoader = new Preloader();
//addChild(_preLoader);
}
private function onLoadingAction(e:ProgressEvent):void
{
// trace(e.bytesLoaded, e.bytesTotal);
}
First of all your event listeners attached to the loader instances before the last one (e.g. loaders 0 to 3) are there. They will still be there for a long long time. Remove them!
ActionScript is a very nice language - use it's power :)
for (i=0; i < img_array.length; i++)
{
_loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,
function onLoadingAction(e:ProgressEvent):void
{
trace((e.target as LoaderInfo).loader.name);
//Do whatever you want to do here like removing event listener
if ((e.target as LoaderInfo).bytesLoaded == (e.target as LoaderInfo).bytesTotal)
{
(e.target as LoaderInfo).removeEventListener(ProgressEvent.PROGRESS, onLoadingAction);
trace("Event listener for " + (e.target as LoaderInfo).loader.name + " removed ");
}
}, false, i * 1000 /* you can use priority if want to maintain some order in event handling*/);
}
ActionScript provides you with the ability to name inline functions and to have a reference to them. Use this approach when you don't need to keep a reference to some object.
Good luck and have fun!
for (i=0; i < _xmlContents.img_array.length; i++)
{
_loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,
onLoadingAction);
//Event.COMPLETE listnere
//error Handler
_preLoader = new Preloader();
_bgBox.addChild(_preLoader);
}
This way is solves my problem.. But I don't think so, it's a good way.
I dont think the array is necessary and I think you'll be on a better track adding 4 instances of the preloader by calling it from Event.INIT as opposed to repeatedly adding it by using ProgressEvent.PROGRESS.
You your _loader object is defined inside the function and not referenced anywhere else, so it is garbage collected after the function ends. Create an array of loaders and push the loaders to them each time.
private var _loadersArray:Array=[]; //outside the function
for (i=0; i < _xmlContents.img_array.length; i++)
{
// create new loader instance, not use a global one
var _loader:Loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadingAction);
_loadersArray.push(_loader);
//Event.COMPLETE listnere
//error Handler
}
private function onLoadingAction(e:ProgressEvent):void
{
_preLoader = new Preloader();
//addChild(_preLoader);
// get current loader instance
var _loader:Loader = e.target.loader;
trace(_loader.name);
}

How can I use addEventListener and return something that's changed from it in Actionscript?

private function getTitle(src:String):String{
var urlLoader:URLLoader = new URLLoader();
var rssURLRequest:URLRequest = new URLRequest(src);
var rss:XML = new XML;
var t:String = src;
urlLoader.addEventListener(Event.COMPLETE,
function(event:Event):void{
rss = XML(urlLoader.data);
t = rss.channel.title.toString();
});
return t;
}
I'm aware that this code doesn't work because the anonymous function doesn't work until after t is returned. How would I make it so that it works?
You won't be able to return the loaded data from this method. The reason for this is because the loading is asynchronous and doesn't not block the execution of subsequent code. Your best option is to move the vars out of the scope of the function and to write a second function to handle the COMPLETE event.
Something like the following should work:
var rss:XML;
var t:String;
var path:String = "some path";
var urlLoader:URLLoader = new URLLoader();
private function getTitle(src:String):String
{
urlLoader.load( new URLRequest( src ) );
urlLoader.addEventListener(Event.COMPLETE, onComplete );
}
private function onComplete(event:Event):void
{
rss = XML(urlLoader.data);
t = path + rss.channel.title.toString();
}
I realize that this doesn't really answer the question directly, though it is the best practice for handling data loading. If you really want to stop any code from executing before the data is loaded, it may be possible use a while loop after the addEventListener line to halt the player from until the data is loaded. This should probably be considered a not so elegant hack though.
private function getTitle(src:String):String
{
var urlLoader:URLLoader = new URLLoader();
var rssURLRequest:URLRequest = new URLRequest(src);
var rss:XML = new XML;
var t:String = src;
var complete:Boolean;
urlLoader.addEventListener(Event.COMPLETE,
function(event:Event):void
{
rss = XML(urlLoader.data);
t = rss.channel.title.toString();
complete = true;
});
while( !complete ) { /* sleep hack */ }
return t;
}
I haven't tested this, but it seems like it could work. The first example is recommended.

ActionScript 3.0 Getting the index [CLICK event] of a loaded movie Clip image

I am loading a set of thumbnail images from an array [hard coded] into a movieclip symbol on the stage. I have two arrays with the thumbnail and the full size image having the same index number. In many examples, "event.currentTarget.contentLoaderInfo.url" returns the full path to the image selected. i just want the index number.
Adobe does not make is easy to figure out what other properties are available to me from the contentLoaderInfo. Is 'SelectedIndex' or something like that available?
Where does an inspiring AS programmer find the contentLoaderInfo properties and or methods available? Is url the only thing that us usable here?
Is there a better approach?
Thanks in advance.
Edit:
var thumbnails:Array = ["tn_2010OpenHouse_00.jpg","tn_2010OpenHouse_01.jpg"];
var images:Array = ["2010OpenHouse_00.jpg","2010OpenHouse_01.jpg"];
var thumbX:Number = 10;
var thumbY:Number = 623;
var loader:Loader = new Loader();
loader.load(new URLRequest("images/" + images[0]));
addChild(loader);
loadThumbs();
function loadThumbs():void
{
var thumbLoader:Loader;
var container:Sprite = new Sprite();
container.width = 100;
addChild(container);
container.buttonMode = true;
for (var i:uint = 0; i < thumbnails.length; i++)
{
thumbLoader = new Loader();
thumbLoader.load(new URLRequest("images/" + thumbnails[i]));
thumbLoader.x = thumbX;
thumbLoader.y = thumbY;
thumbX += 100;
container.addChild(thumbLoader);
thumbLoader.addEventListener(MouseEvent.CLICK, thumbClicked);
container.width += 100;
addChild(thumbLoader);
}
stop();
}
function thumbClicked(ev:MouseEvent):void
{
//weltraumpirat's example
var index:int = thumbnails.indexOf ( ev.target.contentLoaderInfo.url );
trace("Index= "+ index);
//trying a different approach as well
index = thumbnails.indexOf ( ev.currentTarget.contentLoaderInfo.url );
trace("Index= "+ index);
loader.load(new URLRequest("images/" + images[index]));
}
Output:
Index= -1
Index= -1
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.
The contentLoaderInfo property returns a LoaderInfo class. You can view its properties here:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/LoaderInfo.html
You can use array.indexOf() to return an object's index. Since I don't know the rest of your code, here's an approximate example:
function thumbClicked (ev:MouseEvent) : void {
var index:int = thumbnails.indexOf ( ev.target.contentLoaderInfo.url );
loader.load ( new URLRequest (fullSizeImages[index]) ) ;
}
Edit:
Since I didn't know the exact code you were using, I assumed you store the entire path to the picture in your array. In your code, you prepend "images/", so the code should be:
function thumbClicked (ev:MouseEvent) : void {
var index:int = thumbnails.indexOf ( ev.target.contentLoaderInfo.url.substring (7));
// 7 is the length of "images/", so substring returns only the filename part.
loader.load ( new URLRequest (fullSizeImages[index]) ) ;
}