Load multiple images in Air not working properly? - apache-flex

So I made a drag and drop app where I can drop in an image, it get's resized and saved automatically on the desktop.
But now I want to do the same when there are multiple images dragged in.
This is where I'm stuck...
private function onDrop(e:NativeDragEvent):void
{
trace("Dropped!");
var dropfiles:Array = e.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
for each (var file:File in dropfiles){
var ldr:Loader = new Loader();
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
switch (file.extension.toLowerCase()){
case "png" :
ldr.load(new URLRequest(file.url));
trace('png');
break;
case "jpg" :
ldr.load(new URLRequest(file.url));
trace('jpg');
break;
case "jpeg" :
ldr.load(new URLRequest(file.url));
trace('jpeg');
break;
case "gif" :
ldr.load(new URLRequest(file.url));
break;
default:
Alert.show("Kies een geldige afbeelding!");
}
}
}
private function completeHandler(event:Event):void {
var ldr:Loader = Loader(event.target.loader);
var b:Bitmap = Bitmap(ldr.content);
var encoder:PNGEncoder = new PNGEncoder();
var bytes:ByteArray = encoder.encode(resizeimage(b.bitmapData, 600, 600));
//new FileReference().save(bytes, "abc.png");
}
The problem is that I want the completeHandler() to run after every ldr.load() but in my code it only runs once right after the for each loop.
Does anyone know what I'm doing wrong here?

It looks like you the Loaders are only referenced as local variables. After the function onDrop ends, they're probably getting garbage collected. Once that happens, they won't be around to call your event handler. You need to save them somewhere to keep them memory.
I'd save them in an Array. Make a private variable on your class, like this:
private var _loaders:Array = [];
Then, when you create a Loader, save it:
var ldr:Loader = new Loader()
_loaders.push(ldr);
In your completeHandler, or whereever you're done with the Loader, be sure to remove it from the Array:
var index:int = _loaders.indexOf(ldr);
_loaders.splice(index, 1);

Related

Adobe ExtendScript Javascript: Replacing string with a function containing UI elements

New to coding in general, was wondering if its possible to change a content of a variable and update it to the UI with replace() or change()? I believe its much more harder than that but I can't wrap my head around it.
(function(thisObj) {
scriptBuildUI(thisObj)
function scriptBuildUI(thisObj) {
var win = (thisObj instanceof Panel) ? thisObj : new Window("palette", "replaceTest", undefined, 0);
win.alignChildren = ['center','top'];
var myTab = win.add("panel", undefined);
var replacebutton = myTab.add("button",undefined,"Replace");
var myTab2 = "x";
replacebutton.onClick = function(){ myTab2.replace("x",function(){hO()}) };
function hO() {myTab.add("button", undefined,"Retgf")};
win.onResizing = win.onResize = function() {
this.layout.resize();
};
win instanceof Window
? (win.center(), win.show()) : (win.layout.layout(true), win.layout.resize());
}
})(this);
I am not sure what you try to do here. myTab2 is a string variable and .replace() is a string function there. It returns a string. So you are trying to replace the "x" with the content of the function? To what end?
Also, the way i see it, that would just add another button to myTab.
If you could tell me what you try to do, maybe we can find a work-around.
Edit: To just assign your function to myTab2, you can directly do that. And if you want changes to your UI, i think you need to run win.layout.layout(true); to make them vissible. The folowing code adds a button to your tab every time it's clicked.
(function(thisObj) {
scriptBuildUI(thisObj)
function scriptBuildUI(thisObj) {
var win = (thisObj instanceof Panel) ? thisObj : new Window("palette", "replaceTest", undefined, 0);
win.alignChildren = ['center','top'];
var myTab = win.add("panel", undefined);
var replacebutton = myTab.add("button",undefined,"Replace");
var myTab2 = "x";
replacebutton.onClick = function(){ myTab2=hO() };
function hO() {
myTab.add("button", undefined,"Retgf");
win.layout.layout(true);
};
win.onResizing = win.onResize = function() {
this.layout.resize();
};
win instanceof Window
? (win.center(), win.show()) : (win.layout.layout(true), win.layout.resize());
}
})(this);

Music player in Actionscript 3.0

I already added a stop button with autoplay but I need to make it so when you click the button again after you had stopped it, the music starts playing.
Source code:
var music:Sound = new Sound(new URLRequest("calmingsong.mp3"));
var sc:SoundChannel = music.play();
button1.addEventListener(MouseEvent.CLICK, stopMusic);
function stopMusic(e:Event):void
{
sc.stop();
}
If you just want to play the sound over from the beginning, just call the Sound object's play() method again (you get a new SoundChannel object when you do this).
If you'd like to resume playing the sound at the point where the user stopped it, you'll need to add additional variables to store the current "playback state"... Something like this:
var music:Sound = new Sound(new URLRequest("calmingsong.mp3"));
var sc:SoundChannel = music.play();
var startPosition:Number = 0;
var isPlaying = true; // default to true cause you auto play...
button1.addEventListener(MouseEvent.CLICK, togglePlayback);
function togglePlayback(e:Event):void
{
if (isPlaying)
{
startPosition = sc.position;
sc.stop();
isPlaying = false;
}
else
{
sc = music.play(startPosition);
isPlaying = true;
}
}

Flex: assigning events to dynamically created buttons

Thanks to wezzy and the others who helped me out the past couple of days. I've been taking their advice and trying to figure the rest out on my own but I'm stuck again.
I have a txt file structured like this:
button1_label
button2_label
button3_label
My program creates these 3 buttons at runtime and places them in a group.
protected function onLoaded(e:Event):void {
var myArrayOfLines:Array = e.target.data.split(/\n/);
var tempBtn:Button;
for(var i:Number = 0;i < myArrayOfLines.length;i++){
var j:Number = i+1;
tempBtn = new Button();
tempBtn.id = "btn" + i;
tempBtn.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void{
var index:uint = parseInt(evt.currentTarget.id.replace("btn", ""));
//TextArea code will go here
trace(text); // Traces null
});
tempBtn.label = myArrayOfLines[i];
btnArray.push(tempBtn);
group.addElement(btnArray[i]);
}
}
Now what you can see from my code is that I'm attempting to make each button print a string to a textarea. I've structured my new buttons.txt like this:
button1_label
"Hello"
button2_label
"Goodbye"
button3_label
"Come again"
So what I want to do is have button1 print "Hello". All the lines of the .txt file are pushed into myArrayOfLines. Thanks in advance for any help.
EDIT
Full explanation
Sorry I was trying to simplify my question, guess I made it harder to understand. I made a text editor than runs client side, no server. I have a set of buttons that insert predefined phrases into the TextArea Each button has a listener that does myTextArea.insert("sometext"); (but different text for each button. Users have requested the ability to create their own buttons to insert there own strings. I figured I would have a couple og textinputs where a user could define a label, and a String that would be inserted into the textarea on button click. I would write the label to one line, then the String to the next line. Right now I created a buttons.txt file with this format to see if it would work.
FINAL EDIT: WORKING CODE
public function setupBtns(e:Event):void{
var file:File = File.documentsDirectory.resolvePath("buttons.txt");
var stream:FileStreamWithLineReader = new FileStreamWithLineReader();
stream.open(file, FileMode.READ);
while(stream.bytesAvailable) {
// this line contains the headers like button1_label etc.
var label:String;
// this line contains the string
if(stream.bytesAvailable) {
// this line contains the actual label like "Hello";
label = stream.readUTFLine();
line = stream.readUTFLine();
// strip off the first and last character as they are double quotes
line = line.substring(1, line.length-1);
var tempBtn:Button = new Button();
tempBtn.addEventListener(MouseEvent.CLICK, btnListener);
tempBtn.label = label;
tempBtn.name = line;
btnArray.push(tempBtn);
for(var i:Number = 0;i<btnArray.length;i++){
group.addElement(btnArray[i]);
}
}
}
}
protected function btnListener(e:MouseEvent):void{
mainTextField.insertText(e.target.name);
trace(e.target.name);
}
Try this out and let me know how it works out...
EDIT: (try new code below)
protected function onLoaded(e:Event):void {
var myArrayOfLines:Array = e.target.data.split(/\n/);
var tempBtn:Button;
for(var i:Number = 0;i < myArrayOfLines.length;i=i+1){
var j:Number = i+1;
tempBtn = new Button();
tempBtn.id = "btn" + i;
tempBtn.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void{
myTextArea.text += '\n' + myArrayOfLines[j];
});
tempBtn.label = myArrayOfLines[i];
btnArray.push(tempBtn);
group.addElement(btnArray[btnArray.length-1]);
}
}
first: don't use anonymous functions as listeners in actionscript - if you yo, you can't remove them later on.
instead use a class method like this:
tempBtn.addEventListener(MouseEvent.CLICK, onMouseClick);
private function onMouseClick(evt:MouseEvent):void
{
if (evt.currentTarget == button1_label)
{
// do sth for btn1
}
else if (evt.currentTarget == button2_label)
{
// do sth for btn2
}
// ...
}
edit
just saw, that you are using IDs, then just change the above code to sth like this:
if (evt.currentTarget.id == "btn1")

as3 duplicating a text field without removing it from stage

I am trying to duplicate a text field. First I get the text with a mc.getChildAt(0) and then copy all the contents into a new textfield. The problem I am having is that getChildAt removes the textfield from the movieclip it is in. How to I get the properties of the textfield without moving it? Or maybe it is something else and what I am doing is fine. Any insight would be a huge help...
var dupeTField:MovieClip = duplicateTextField($value.sourceImg.getChildAt(0));
private function duplicateTextField($textField):MovieClip
{
var currTextField:TextField = $textField;
var dupeTextHolder:MovieClip = new MovieClip();
var dupeTextField:TextField = new TextField();
dupeTextField.text = currTextField.text;
dupeTextField.textColor = currTextField.textColor;
dupeTextField.width = $textField.width;
dupeTextField.height = $textField.height;
dupeTextHolder.addChild(dupeTextField);
return dupeTextHolder;
}
Use something like this:
package com.ad.common {
import flash.text.TextField;
import flash.utils.describeType;
public function cloneTextField(textField:TextField, replace:Boolean = false):TextField {
var clone:TextField = new TextField();
var description:XML = describeType(textField);
for each (var item:XML in description.accessor) {
if (item.#access != 'readonly') {
try {
clone[item.#name] = textField[item.#name];
} catch(error:Error) {
// N/A yet.
}
}
}
clone.defaultTextFormat = textField.getTextFormat();
if (textField.parent && replace) {
textField.parent.addChild(clone);
textField.parent.removeChild(textField);
}
return clone;
}
}
I think you'll find your problem is somewhere else. getChildAt does not remove its target from its parent, and the function you posted works as advertised for me, creating a duplicate clip without affecting the original.
private var dupeTField:MovieClip;
private function init():void
{
//getChildAt will return a DisplayObject so you
//should cast the return DisplayObject as a TextField
var tf:TextField = $value.sourceImg.getChildAt(0) as TextField;
dupeTField = duplicateTextField(tf);
//don't forget to add your duplicate to the Display List
//& make sure to change the x, y properties so that
//it doesn't sit on top of the original
addChild(dupeTField );
}
private function duplicateTextField(textField:TextField):MovieClip
{
var dupeTextHolder:MovieClip = new MovieClip();
var dupeTextField:TextField = new TextField();
//if you pass a TextField as a parameter, you don't need to
//replicate the instance inside the function, simply access the
//parameter directly.
//You may consider copying the TextFormat as well
dupeTextField.defaultTextFormat = textfield.defaultTextFormat;
dupeTextField.text = textField.text;
dupeTextField.textColor = textField.textColor;
dupeTextField.width = textField.width;
dupeTextField.height = textField.height;
dupeTextHolder.addChild(dupeTextField);
return dupeTextHolder;
}

FLEX - Load an image of type class instance

I have a barCode class that is used to generate an image of a barCode. I create an instance of this class and it works as expected for example:
var myBarCodeInstance:barCode = new barCode();
var myBarCodeImg:Image = new Image();
myBarCodeImg.source = myBarCodeInstance;
Using this code the image appears and works fine. However, my question is how do I implement a loader on this image that will fire an event when the image is fully loaded and ready for processing? (I am running into null problems with the image not being fully loaded before attempting to access its contents).
Something like the below:
var loader:Loader;
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(e:Ev ent):void{
myBarCodeImg.source = e.currentTarget.content;
// further processing here
});
loader.load(new URLRequest(encodeURI(“image.jpg“)));
but i dont know what to insert in place of the "image.jpg" part due to my image being an instance of a class and not an actual jpg file.
Image dispatches a complete event - try listening for that event:
var myBarCodeInstance:BarCode = new BarCode();
var myBarCodeImg:Image = new Image();
myBarCodeImg.addEventListener(Event.COMPLETE, completeHandler);
myBarCodeImg.source = myBarCodeInstance;
function completeHandler(e:Event):void
{
var img:Image = e.currentTarget as Image;
var barcode:BarCode = img.content as BarCode;
/* process it */
}

Resources