Thursday, February 4, 2010

Dynamically Showing Embedded Images in Flex

Dynamically Showing Embedded Images

Several resources will show how easy it is to embed an Image in Flex. You can embed inline, via meta tag, and another way (which I forgot right now). Regardless of any of these ways, they assume you always know when you are going to be using your embedded image, and don't have much flexibility if you want to dynamically change the image for a component based on something as arbitrary as mouseOver.

If you look at the profuse examples when googling, you come across many that show you how to embed inline:
<
<mx:Image id="img" source="{@Embed(source=assets/img.jpg)}" />

Or you can embed an image via metatag inside your class, and then call it explicitly in your component:

<mx:Metadata>
[Embed(source="assets/icons/myImg.png")]
public static const myImg:Class;
</mx:Metadata>

<mx:Image id="img" source="{myImg}" />

Or you can embed the object in another class, foo, and then reference it in your Image:

import location.for.assetClass.foo;
...
<mx:Image id="img" source="{foo.myImg}" />

More often than not, I like to go the third route, mainly because I want to be able to access the images from another class, or from multiple components. Now knowing how to embed images is great, but my quandary was, "What do I do if I have 30 or 100 small icons/images that I want to embed, and the component that is using the image doesn't know the embedded image to use until runtime, i.e. itemRenderer, or dynamically generated object using static icons (recycled buttons…)?"

For this example, I am using a simple list, with a custom itemRenderer. The list's dataprovider has an id and some other information for each record. In my customItemRenderer, I can get the data.id and use that to determine the picture I wanted to use. The only problem was, I have know idea how to call foo.myImg dynamically. I tried getClassByName(data.id), but it refused to cast my object to a class or would return null.

I really thought that because I had embedded my image, then cast it as a class, I'd be able to call it by name. Bummer!

Next I setup an array and a method inside of class foo. The array, called myAssets, is an array of all the classes inside of foo. The function was called from the itemRenderer, in init() to get the class for the image to assign it, as a ByteAsset, to the source for the image. Inside of foo, I have the following:

public function getClass(str:String):Class{
var s:String = "[class IconAssets_" + str + "]";
for (var i:int = 0; i < myAssets.length; i++){
var t:String = myAssets[i].toString();
trace(t);
if(s == t){
return myAssets[i];
}
}
return null;
}

I would pass in the data.id to the getClass(), and the return would be the class I needed. This actually worked, but seemed quite cumbersome, especially because now I was not only responsible for creating the embedded image, but also adding and maintaining an array for all objects. This seemed a bit extraneous, so I finally hit on a solution that let's me do this quickly and easily.

The most annoying part of all of this is either not knowing the correct syntax (because I'm a music major and am learning coding on my own), wording to search the world of Flex development blogs, or something else involving the continual error between seat and keyboard…

Anyways, the ultimate solution was actually quite easy.

Keeping the foo class with all my embedded images is fine, and in my itemRenderer, for the image, I just need to put the following for the source:

<mx:Image id="img" source="{foo[data.id]}" />

That's it! Probably too much time spent trying to figure this out, but in the end, the class is acting like an object, and I can get any of my "properties" out of the class pretty easily now. I struggled w/ this concept on an earlier project, but now that I have the quick fix to dynamically change the source of an image with an embedded image without having to hard code any values in the component is much, much better. Ok, so lesson learned, the answer wasn't that hard, but I needed to futz with the other possibilities before I could finally get it right. Now that I got it right, I promise I shouldn't get it wrong again*.

*I might, but that's all part of my slow learning.

7 comments:

  1. Useful Article! Good job!

    ReplyDelete
  2. [Embed(source="assets/icons/myImg.png")]
    public static const myImg:Class;


    How to use a URL in this?

    ReplyDelete
  3. sorry anonymous, do you want to deeplink an embedded image into your swf? i'm not sure if you'd want to actually have an embedded image that says [Embed(source="http://whateversite.com/stapleImage.png")]. The point of embedding an image is that the compiler will actually embed the image into your app. If you want a remote image, then use the loader, swfloader, or image classes to import the image. Otherwise, embedding will actually embed the image into your app.

    ReplyDelete
  4. Hi ,
    Can you please share us the complete foo class code?

    Thanks,
    Anjani

    ReplyDelete
  5. Wow
    Too
    Can you please share us the complete foo class code?

    TX!

    ReplyDelete
  6. Work?
    Need full code

    ReplyDelete