I'm working on an Flex application which uses many objects, e.g. LEDs, bulbs, gauges created in Flash. The objects internally consist of several small MovieClips and have their own logic inside. One of the initial requirements was that the objects have to be loaded at runtime, thus they were exported as SWF, not SWC. However, we ran into problem when we tried to duplicate loaded SWF. It turned out that MovieClip class doesn't have neither copying constructor nor method that would allow us to clone existing MovieClip. This way we'd end up loading an object every time from hdd, which involves a lot of overhead. Is it possible that language like ActionScript 3 doesn't have such a mechanism? Have we missed something out? If not, am I correct that the only solution is to use Flash Component Kit, make some custom components and include them as SWC at compile time?
After you load the MovieClip is it possible to use getDefinitionByName() as shown here?
http://livedocs.adobe.com/flex/3/langref/flash/utils/package.html#getDefinitionByName()
You are correct in that there is no built in way to duplicate a movieclip.
There are however work arounds. The esiest way as I see it is to give the movieclips classes.
You don't have to make the actual classes. Just write any name in the class field when setting up linkage on the top most movieclip that needs to be copied.
So a name for you LED movieclip, another name for the bulb etc.
The benifit is that now you have a class that you can initiate objects from.
No when you grap one of the movieclips you can duplicate it with the following method:
public function DuplicateDisplayObject(dO:DisplayObject):DisplayObject
{
if(dO == null)
return null;
var dOClass:Class = Object(dO).contructor;
return DisplayObject(new dOClass());
}
This assumes of cause that you can actually get a hold of one of the movieclips first. And mind you that it doesn't copy the state of the movieclip.
Another more importent note is that this only works if the you link the movieclips to classes. The classes doesn't have to exist (flash will create empty classes for you).
Other solutions could be:
Compiling against the classes without
including them (see the
"external-library-path" tag for the
flex compiler), and load the them at
runtime (from swf).
Compiling against the classes as a
RSL (Runtime Share Library) the swc
will be loaded at runtime.
Adobe has more info on how to do that, should be easy to find on their website.
A more exotic solution would be copy the bytecode of an object. Not sure if that would work with something on the displaylist, properly not.
About the solution using getDefinitionByName():
If I remember correctly you still need to give the movieclips fake classes, since getQualifiedClassName only returns MovieClip class. But I could be wrong.
Another solution:
private function duplicateImg(sourceLoaderInfo:LoaderInfo, target:Image):void
{
var ba:ByteArray = sourceLoaderInfo.bytes;
var dupCliploader:Loader = new Loader();
dupCliploader.contentLoaderInfo.addEventListener(
Event.COMPLETE, bytesLoaded);
dupCliploader.loadBytes(ba);
}
private function bytesLoaded(event:Event):void
{
var mc:MovieClip = event.currentTarget.content as MovieClip;
_img.source = mc;
_img.width = mc.width;
_img.height = mc.height+5;
}
Related
I have a Flex application which references a separate MXML file as a template for a custom component. I create instances of the component dynamically several times in my program, but I need to get a handle that will allow me to modify that instance of the component as desired.
I pass specific information to this component on instantiation using bindable public variables in the component's MXML file. I add it to my main program using addChild().
I want to update the component's progressbar as necessary and I want to remove it from the box to which I addChild'd it.
What's the easiest/best way to get a variable that will give me predictable access to each component so I can easily manipulate the components as necessary? Some research suggests creationComplete, but I decided it was faster to just ask than to go through lots of different experiments and come up blank.
Thanks for all the help. : )
Can you not just keep a list of your components in an array? Presumably you have an object reference when you create them and call addChild() on their parent. Why not just put them in an array at the same time?
var list_of_controls:Array = new Array();
var new_Object:<yourType>;
new_Object = new <yourType>();
parent.addChild(new_Object);
list_of_controls.push(new_Object);
then you can get at them...
var my_Object:<yourType>;
for each (my_Object in list_of_controls)
{
// do something
}
You would have to make sure you dispose of them properly when you re done because the reference in your array would keep them in existence until cleared.
If you decide that you want to use getChildren() instead - which you could - take the time to read the documentation because I think it returns a new array with each call.
I hope that helps.
This is my first time here, but I already found some good answers here, so I'd like to thank everyone.
I'm developping a small Flex application and I want to instantiate every class from a package into an array, so I can parse it afterwards. To clarify, I'm trying to ease a plugin management system for my application, with the old canProcess/doProcess routine :
My plugins are all in ONE package, including an abstract plugin class. First, I create one instance of every classes in this package (that's where I need help) and put them in an array. Then, whenever I need a plugin for an item, I parse every plugin class in my array with the canProcess method (the item is the parameter). If one plugin says yes, then I send the item to the doProcess method and stop parsing the array.
I know I could implement by hand every class in my package, but I'd prefer not bothering to do it.
Has anyone an idea ?
Thx
AS3 reflection doesn't allow you to list all classes in a package. You will have to write the class names to an (xml) file at the server, load it and then use getDefinitionByName to get Class objects from those strings and then instantiate them.
Consider the sample xml file:
<root package="boris.ratak">
<className>Plugin1</className>
<className>Plugin2</className>
<className>Plugin3</className>
</root>
load it with URLLoader and parse it like:
import flash.utils.getDefinitionByName;
var pack:String = String(xml.#package) + ".";
for each(var cl:String in xml.className)
{
var name:String = pack + String(cl.text());
var Type:Class = getDefinitionByName(name) as Class;
pluginArray.push(new Type());
}
I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?
You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});
I think you need to respond to the "PropertyChanged" event.
If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");
After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html
You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.
I have an FLA file with objects in the library which I have set to be "classes" (In CS3, right click an item in the library select properties, make sure it's set to export for action-script, and has a class name)
For this exercise, let's call the class "MyClass"
If I publish that FLA to an SWC and SWF:
I can load the SWC statically, and instantiate "MyClass" by simply doing:
var inst:MyClass = new MyClasS();
Now, the problem: I'd like to be able to do this at runtime by loading the SWF file using a loader object.
I understand how to access instances which have been created by hand in the FLA before publishing, but what I want to be able to do, is create new instances of the class "MyClass".
I can get a "MovieClip" representing the swf file, I can add it to my displaylist, but I can't seem to get at the classes contained therein. (I hope this makes sense)
Any suggestions for how to attack this would be much appreciated.
Edit : Format code
To complete Christian's answer:
var cls : Class = loader.contentLoaderInfo.applicationDomain.getDefinition("ClassName");
var instance : Object = new cls();
Additionally, it's worth noting that you won't get strong typing (ie. it must be declared as Object) unless the class implements interface which is also defined in your main application. You will then be able to declare the instance variable as the interface and have compile-time access to it's members.
Have a look here; you should be able to extract a class reference by using Loader.contentLoaderInfo.applicationDomain.getDefinition("MyClass").
MXML lets you do some really quite powerful data binding such as:
<mx:Button id="myBtn" label="Buy an {itemName}" visible="{itemName!=null}"/>
I've found that the BindingUtils class can bind values to simple properties, but neither of the bindings above do this. Is it possible to do the same in AS3 code, or is Flex silently generating many lines of code from my MXML?
Can anyone duplicate the above in pure AS3, starting from:
var myBtn:Button = new Button();
myBtn.id="myBtn";
???
The way to do it is to use bindSetter. That is also how it is done behind the scenes when the MXML in your example is transformed to ActionScript before being compiled.
// assuming the itemName property is defined on this:
BindingUtils.bindSetter(itemNameChanged, this, ["itemName"]);
// ...
private function itemNameChanged( newValue : String ) : void {
myBtn.label = newValue;
myBtn.visible = newValue != null;
}
...except that the code generated by the MXML to ActionScript conversion is longer as it has to be more general. In this example it would likely have generated two functions, one for each binding expression.
You can also view the auto-generated code that flex makes when it compiles your mxml file, by adding a -keep argument to your compiler settings. You can find your settings by selecting your projects properties and looking at the "Flex Compiler" option, then under "Additional compiler arguments:" add "-keep" to what is already there.
Once done Flex will create a "generated" directory in your source folder and inside you'll find all teh temporary as files that were used during compilation.
I believe flex generates a small anonymous function to deal with this.
You could do similar using a ChangeWatcher. You could probably even make a new anonymous function in the changewatcher call.