Delay in opening combobox when using an item renderer

I am trying to figure out a way to improve the performance of a combobox with an item render in it. When I use a combobox without a custom item renderer the drop down opens quickly and the combobox is very responsive. When I add a simple item renderer it now takes a second before opening the combobox. It's like its creating all the children and then caching them. After that the combobox opens fine, until you select an item. Then opening the combobox takes a while again too.

Here is a sample application that demonstrates the issue:

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
            height="100%" width="100%" xmlns:local="*"
    >
<mx:Script><![CDATA[
    import mx.collections.ArrayCollection;

    public var dataProvider:ArrayCollection = new ArrayCollection(
               [{label:"test1"}, 
                {label:"test2"}, 
                {label:"test3"}]);

    ]]></mx:Script>

<!-- combobox with item renderer, this has a delay when opening -->

<mx:ComboBox width="200"
             dataProvider="{dataProvider}">
    <mx:itemRenderer>
        <mx:Component>
            <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
                <mx:Label text="'item renderer' + {data.label}"/>
            </mx:HBox>
        </mx:Component>
    </mx:itemRenderer>
</mx:ComboBox>

<!-- default combobox, this works fine -->

<mx:ComboBox width="200"
             dataProvider="{dataProvider}"/>
</mx:Application>

What am I missing here? It doesn't seem like this should be happening.


After spending some time trying to find a solution, what I found is if there is only one "clean" child component gets defined in <mx:Component> , the performance will be much better.

So the following gives good performance:

<mx:Component>
    <mx:HBox /> ( "clean" )
</mx:Component>

and this one gives bad performance (the delay):

<mx:Component>
    <mx:HBox>
        <mx:Label /> ( not "clean" )
    </mx:HBox>
</mx:Component>

To solve this problem, we can create the child components by using codes:

<mx:itemRenderer>
    <mx:Component>
        <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
            <mx:Script>
                <![CDATA[
                    import mx.controls.Label;

                    // Define the label instance
                    private var lbl:Label = new Label();

                    // Add the label as a child component
                    override protected function createChildren(): void {
                        super.createChildren();
                        addChild( lbl );
                    }

                    // Set the data
                    override public function set data( value:Object ): void {
                        super.data = value;
                        if ( value ) {
                            lbl.text = value.label;
                        }
                    }
                ]]>
            </mx:Script>
        </mx:HBox>
    </mx:Component>
</mx:itemRenderer>

Demo: https://github.com/jeantimex/flex-combobox-itemrenderer

Hope that helps!


This may be a step in the right direction; but I can't guarantee a solution. I suggest you start by re-writing your itemRenderer not to use binding. I've solved a lot of memory leaks for paying clients by teaching them that. Binding is also known to be a performance heavy operation in itemRenderers; so I would recommend that it get removed from the operation.

Instead of using Binding in an itemRenderer, I prefer to respond to the dataChange method.

I also recommend that you remove the HBox from your itemRenderer; as you should be able to use the label as is:

<mx:ComboBox width="200"
             dataProvider="{dataProvider}">
    <mx:itemRenderer>
        <mx:Component>
            <mx:Label dataChange="onDataChange(event)">
              <mx:Script>
                 protected function onDataChange(event:Event):void{
                    this.text='item renderer' + data.label;
                 }
              </mx:Script>
            </mx:Label>
        </mx:Component>
    </mx:itemRenderer>
</mx:ComboBox>

I'm not sure if that will address any performance issues. There are a lot of oddities w/ the Flex ComboBox that I've had to work through while creating the Flextras AutoComplete.

Memory tells me that in Flex 3.4 (and before) every time the drop down was closed; it was destroyed--essentially not cached in memory. This changed in Flex 3.5 whereas they started caching it and re-using the same drop down object. I wonder if your perceived performance issues are related to that somehow.


Edit 1: Based on complaints about this post not addressing the underlying issue; I put together this test case based on the original posters code and my suggested changes. My suggested changes do indeed solve the issue the original poster was having. Here is the source behind the sample:

    [Bindable]
    public var dataProvider:ArrayCollection = new ArrayCollection(
        [{label:"test1"}, 
            {label:"test2"}, 
            {label:"test3"}]);

]]></mx:Script>

<!-- combobox with item renderer, this has a delay when opening -->

<mx:VBox>

<!-- ComboBox provided by original poster -->
<mx:ComboBox width="200"
             dataProvider="{dataProvider}">
    <mx:itemRenderer>
        <mx:Component>
            <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml">
                <mx:Label text="'item renderer' + {data.label}"/>
            </mx:HBox>
        </mx:Component>
    </mx:itemRenderer>
</mx:ComboBox>


<!-- ComboBox with rewritten itemRenderer; which does not exhibit the problem -->
<mx:ComboBox width="200"
             dataProvider="{dataProvider}">
    <mx:itemRenderer>
        <mx:Component>
            <mx:Label dataChange="label1_dataChangeHandler(event)">
                <mx:Script>
                    <![CDATA[
                        import mx.events.FlexEvent;

                        protected function label1_dataChangeHandler(event:FlexEvent):void
                        {
                            this.text = "item renderer" + data.label;
                        }

                    ]]>
                </mx:Script>
            </mx:Label>
        </mx:Component>
    </mx:itemRenderer>
</mx:ComboBox>


<!-- default combobox, this works fine -->

<mx:ComboBox width="200"
             dataProvider="{dataProvider}"/>
</mx:VBox>


Edit 2: devshorts says my code does not work if we create the component as a stand alone itemRenderer; however I don't understand why not.

Here is the code for the stand alone itemRenderer, named com.flextras.listRenderers.CustomLabelRenderer:

<?xml version="1.0" encoding="utf-8"?>
<mx:Label xmlns:mx="http://www.adobe.com/2006/mxml" dataChange="label1_dataChangeHandler(event)">

    <mx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            protected function label1_dataChangeHandler(event:FlexEvent):void
            {
                this.text = "item renderer" + data.label;
            }

        ]]>
    </mx:Script>    
</mx:Label>

And here is some code to add to the mail application above to test it:

<!-- ComboBox, like above with the itemRenderer as a separate component -->
    <mx:ComboBox width="200"
             dataProvider="{dataProvider}" itemRenderer="com.flextras.listRenderers.CustomLabelRenderer" />

I updated the application at my provided link to include this new ComboBox instance; it is now the 3rd on the list.

链接地址: http://www.djcxy.com/p/34584.html

上一篇: Flex文本项呈示器多行

下一篇: 使用项目渲染器时延迟打开组合框