ImageCache, a cheap way to cache images in Adobe Flex

In a previous post about Ely’s SuperImage, I mentioned that we decided to implement a simpler method for caching images within a Flex application. ImageCache is a simple class file that extends the Image control by adding the ability for the control to cache bitmap data. Unlike SuperImage, ImacheCache can handled SWF files, it just lacks all the bells and whistles of SuperImage, though image flicker is eliminated.

Using ImageCache

ImacheCache is just two class files. ImageCache.as which extends the Image control and ImageCachUtility.as which is a Singleton class that used by Imagecache to store and retrieve the BitmapData for cached images. Bitmap data is stored in a regular ArrayCollection object and retrieved by using the full path to the image (because image names can be duplicates but paths should be unique). Once the amount of cached images reaches the cache limit, the controls works on first in first out, dropping off older cached images as it caches new ones and staying within the cache limit. SWF fiels are not actually cached and will load as normal. You just use ImageCache like you would the Image control within your project:


<controls:ImageCache cacheLimit="200" id="image" source="{data.url}" width="100" height="100" complete="imageComplete()" ioError="imageError()" xmlns:controls="com.thanksmister.controls.*"/>

Cache Limit

ImageCache has a property called “cacheLimit” which tells the control how many images to cache. Because caching images can reduce the performance of your application. The larger the cache limit value, the more memory your application will use because it is storing all the Bitmap data in memory. If you want to reset the image cache, create an instance of the ImageCacheUtility and call the method “clear()”. ImageCache has been used and tested extensively for large media sites. Below is a screen shot of images from Flickr, the list on the left uses the standard Image control, the list on the right uses the ImageCache control. It seems that there may be some security issue when loading Flickr images without doing a proxying the images. Run the example locally or proxy Flickr images for best results.

Example

Code

You can download the code for the above project here. You will need your own Flickr API key to make the application work.

-Mister

Bubble ItemRenderer Events in Flex

Often times I find myself needing to bubble events from an itemRenderer to the parent control.   This is especially important when using a CairngormEventDispatcher.  Events should be used by the views, rather than having them nested within the itemRenderer.  This is commandment #10 on Jesse Warden’s post 10 Tips For Working With Cairngorm (or the way Jesse writes about it, 10 Thinks I Love to Hate About Cairngorm). 

I try, when time permits, to adhere to this practice as it can be a big pain in the ass to dig down deep within your structure hunting for events. This also makes the code more reusable as you are not having to customize itemRenderers when they need to pass different events, this can be handled in the view instead, nice an neat.

For my example I extended a List control to have a new event “menuClicked”.  

List:

package com.mister.controls
{
    import mx.controls.List;

    [Event(name="menuClick", type="mx.events.MenuEvent")]

    public class List extends mx.controls.List
    {
        public function List()
        {
            super();
        }
    }
}

I used an MXML itemRenderer instead of a class, just because I like to have the layout ease that comes with MXML.  

ItemRenderer:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="24">
    <mx:Script>
        <![CDATA[
            import mx.events.MenuEvent;
            import mx.controls.Menu;

            private function createMenu(event:Event):void
            {

                  var menu:Menu = Menu.createMenu(menuButton, menuData, false);
                      menu.labelField="@label";
                      menu.addEventListener(MenuEvent.ITEM_CLICK, bubbleMenuEvent);

                   var point:Point = new Point();
                       point.x = menuButton.x;
                    point.y = menuButton.y;
                        point = this.localToGlobal(point);

                menu.show(point.x, point.y);
            }

            private function bubbleMenuEvent(event : MenuEvent):void
            {
                var e : MenuEvent = new MenuEvent("menuClick", true, true, null, null, event.item, this, data.label);
                dispatchEvent(e);
            }

        ]]>
    </mx:Script>

    <mx:XML id="menuData">
        <root>
            <menuitem label="Edit" data="edit"/>
            <menuitem label="Delete" data="delete"/>
            <menuitem label="Save" data="save"/>
           </root>
    </mx:XML>

    <mx:Label text="{data.label}" verticalCenter="0" x="10"/>
    <mx:Button id="menuButton" click="createMenu(event)" verticalCenter="0" right="10" width="54" label="menu"/>

</mx:Canvas>

I made an clickable event using a Menu control and bubbled the event to the view of my application.    

Application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    creationComplete="initApp()" viewSourceURL="srcview/index.html">

    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.MenuEvent;
            import mx.collections.ArrayCollection;

             private var listArray:Array=[
                 {label: "Label One", data: 1},
                 {label: "Label Two", data: 2},
                 {label: "Label Three", data: 3},
                 {label: "Label Four", data: 4},
                 {label: "Label Five", data: 5}];

             [Bindable]
            public var dp:ArrayCollection;

            public function initApp():void
            {
                dp = new ArrayCollection(listArray);
            }

            private function handleMenuEvent(event : MenuEvent):void
            {
                Alert.show("Menu Item Clicked : " + event.item.@data + " and List Row Label : " + event.label);
            }
        ]]>
    </mx:Script>

    <controls:List dataProvider="{dp}" menuClick="handleMenuEvent(event)"
        itemRenderer="com.mister.custom.BubbleItemRenderer"
        width="450" height="240" horizontalCenter="0" top="50"
        xmlns:controls="com.mister.controls.*"/>

    <mx:Label y="24" text="Bubble Event from List ItemRenderer" horizontalCenter="0" fontWeight="bold"/>

</mx:Application>

This example is simple, but you can see how you can extend this to types of itemRenderers.

View Source (right-click) and Example

Download Example

-Mister

Flex List Control with Editable InputText ItemRenderer

I think the title of this post says it all.   I needed a InputText for the label of images in my TitleList control.  I wanted users to click on the text and edit it in place.  I started out creating a simple ItemRenderer with a InputText and skinned the InputText to look like a regular label until you click to edit the information.   I then decided to go a little further and make a custom control based on InputText which did all the style changes and kept broadcast a change event only when the text actually changes.   I also removed the extension from any titles (like myimage.jpg becomes myimage instead).

This was all fine and dandy until I used the component within the TileList.   You see, the List controls allow Alphanumeric keys as a form of navigation.  This is really cool because if you have images and titles below them, you can skip from image to image by typing the first letter of the image title.  However, this conflicted with the InputText.  Each time I clicked on a title to edit and started to type, the TitleList would navigate based on the character I typed.   This was not so cool. 

I ended up with a simple solution:

event.stopImmediatePropagation();

I used stopImmediatePropogation to prevent my keyDown events on the InputText from bubbling up to the List control.  Problem solved!  Here is a working example, right-click on the Flex application to get the code (I can’t tell you how many people post messages about how to get the Flex source code from examples, I mean, if you have to ask….).

 

ItemRendererEditor Example and Source

 

- Mister

Using XML with DataGrid itemRenderers

For a recent project I needed a way to create a ComboBox in a DataGrid component that was populated by an XML file.   I found an older example by Brendan Meutzner that used an ArrayCollection.

This was a good starting point, so I modified and added to the file to create an example that uses a ComboBox, CheckBox, and NumericStepper within the DataGrid and populated by an external XML file.  The real trick is getting the XML file to be updated when changes to the itemRenderer occurred.

This is where Brendan’s example helped the most.  Using the editorDataField and renderIsEditor properties of the DataGridColumn, the changes to the itemRenderer were able to update the dataField and the XML file itself.  

Another trick I found interesting was making the the value or selected values of the NumericStepper and CheckBox components Bindable within the itemRenderer code.   You can review the complete file and source code below.

Sample and Source Code

-Mister