ItemRenderer性能优化

http://rss.9ria.com/?p=3640

DataGrid和AdvancedDataGrid应该是Flex企业项目中最常用的组件了,今天开始写一些优化技巧,这些技巧直接影响DataGrid的渲染效率。

ItemRenderer优化。

ItemRenderer是我们在扩展DataGrid单元格功能时需要扩展开发最多的东西,网络上有很多ItemRenderer的例子,但是大部分来说都是从方便开发的角度来使用的ItemRenderer而忽略了效率问题。那么到底何种情况是看似没问题,但实际上却牺牲了效率的呢?比如:

1.我们需要显示一个图片Icon,然后紧接着显示一个label。也许我们会使用下面这样的代码(摘自Adobe开发者博客2008年的一篇文章,http://www.adobe.com/devnet/flex/articles/itemrenderers_pt1.html)

<mx:DataGrid x="29" y="303" width="694" height="190" dataProvider="{testData.book}" variableRowHeight="true">
    <mx:columns>
        <mx:DataGridColumn headerText="Pub Date" dataField="date" width="85" />
        <mx:DataGridColumn headerText="Author" dataField="author" width="125"/>
        <mx:DataGridColumn headerText="Title" dataField="title">
            <mx:itemRenderer>
                <mx:Component>
                    <mx:HBox paddingLeft="2">
                        <mx:Script>
                        <![CDATA[
                            override public function set data( value:Object ) : void {
                                super.data = value;
                                var today:Number = (new Date()).time;
                                var pubDate:Number = Date.parse(data.date);
                                if( pubDate > today ) setStyle("backgroundColor",0xff99ff);
                                else setStyle("backgroundColor",0xffffff);
                            }
                        ]]>
                        </mx:Script>
                        <mx:Image source="{data.image}" width="50" height="50" scaleContent="true" />
                        <mx:Text width="100%" text="{data.title}" />
                    </mx:HBox>
                </mx:Component>
            </mx:itemRenderer>
        </mx:DataGridColumn>
    </mx:columns>
</mx:DataGrid>

这样写从正确与否的角度说完全没有问题,我们可以有两种写法,最简单的就是上面这样,在标签内直接写Itemrenderer。第二种的是创建一个新mxml文件继承于HBox取名叫XXXXRenderer,然后加入Image和Text控件,这种分离的写法也适合动态创建columns时使用。

但是不管是哪种,都已经忽略了效率问题(咱们今天的重点)。在Itemrenderer里使用Container的子类是很消耗资源的做法,相反高效的做法是直接继承UIComponent,在UIComponent里加入Image和Text控件。效率问题在小数据量,只有几列Column的时候是不容易发现的。但是如果你需要一次显示千条甚至万条的数据的,多列column都包含复杂Itemrenderer的话,效率问题就不得不考虑的(你也可以考虑为每个客户购置牛逼的机器)。就这个例子使用UIComponent来说,你会感觉出来Flex在渲染Datagrid时“假死”的时间会缩短,如果你机器太好,感觉不是很明显的话,尝试滑动滚动条,滚动条的平滑程度很真实的反应了两种情况下grid每个Cell的创建时间。

下面是我用UIComponent创建同样ItemRenderer的代码

package renderer
{
	import mx.controls.Label;
	import mx.controls.Text;
	import mx.controls.dataGridClasses.DataGridListData;
	import mx.controls.listClasses.BaseListData;
	import mx.controls.listClasses.IDropInListItemRenderer;
	import mx.controls.listClasses.IListItemRenderer;
	import mx.core.IDataRenderer;
	import mx.core.UIComponent;
	import mx.core.UIComponentGlobals;
	import mx.events.FlexEvent;
	import mx.managers.ILayoutManagerClient;
	import mx.styles.IStyleClient;
 
	import spark.components.Image;
 
	public class DataGridExampleRenderer extends UIComponent implements IListItemRenderer,IDropInListItemRenderer
	{
 
 
		private var _listData:DataGridListData;
		private var _data:Object;
		private var text:Text;
		private var img:Image;
 
 
		public function DataGridExampleRenderer()
		{
			super();
		}
 
 
		override protected function createChildren():void{
			super.createChildren();
			text = new Text();
			img = new Image();
			this.addChild(text);
			this.addChild(img);
		}
 
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth,unscaledHeight);
			text.setActualSize(unscaledWidth/2,unscaledHeight);
			img.setActualSize(unscaledWidth/2,unscaledHeight);
			text.move(unscaledWidth/2,0);
		}
 
		override protected function commitProperties():void
		{
			super.commitProperties();
			if(listData){
				text.text = data[(listData as DataGridListData).dataField];
				img.source = data.img;
			}else{
				text.text = ""
			}
 
 
		}
 
		[Bindable("dataChange")]
		public function get data():Object
		{
			return _data;
 
		}
 
		public function set data(value:Object):void
		{
			_data = value;
			invalidateProperties();
			dispatchEvent(new FlexEvent("dataChange"));
		}
 
 
 
 
		public function get listData():BaseListData
		{
			return _listData;
		}
 
 
		public function set listData(value:BaseListData):void
		{
			_listData = DataGridListData(value);
 
		}
 
 
	}
}

以上是第一种情况,咱们看第二种。

2.习惯性的使用Label,Text,TextArea来作为ItemRenderer来方便改变样式,比如背景,HTML显示等。

比如这个例子http://blog.flexexamples.com/2007/08/20/formatting-a-flex-datagrid-control-using-a-custom-item-renderer/

非常正确的代码,但是却是影响效率的做法,不管是DataGridItemRenderer或者AdvancedDataGridItemRenderer.它们是继承于UITextField.相较于Label,Text或者TextArea来说。相当的轻量级,所以我们只需要继承于DataGridItemRenderer或者AdvancedDataGridItemRenderer,对它就行扩展同样可以实现样式改变,HTML显示的功能。

上面博客中是这么写的

package {
    import mx.controls.Label;
    import mx.controls.listClasses.*;
 
    public class PriceLabel extends Label {
 
        private const POSITIVE_COLOR:uint = 0x000000; // Black
        private const NEGATIVE_COLOR:uint = 0xFF0000; // Red
 
        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
            super.updateDisplayList(unscaledWidth, unscaledHeight);
 
            /* Set the font color based on the item price. */
            setStyle("color", (parseFloat(data.@price) <= 0) ? NEGATIVE_COLOR : POSITIVE_COLOR);
        }
    }
}

而从优化效率的角度来说,可以有个简单的方法这么写,继承DataGridItemRenderer,覆盖validateProperties.(我没实际运行过,不过肯定是没问题的)

override public function validateProperties():void
	{
		super.validateProperties();
		if (listData)
		{
			var grid:DataGrid = DataGrid(listData.owner);
			var column:DataGridColumn = grid.columns[listData.columnIndex];
 
			htmlText = parseFloat(data.@price) <= 0? "<font color='#ff0000'>"+data[column.dataField]+"</font>":data[column.dataField];
		}
	}

总结起来的话就简单的两句点

1.不要使用Container做为ItemRenderer的父类,继承UIComponent.Container能做的,UIComponent也能做(多写一点代码)

2.如果只作为数据显示效果而使用ItemRenderer。不必继承label,Text或者TextArea.直接继承DataGridItemRenderer或AdvancedDataGridItemRenderer进行扩展。

下面的demo展示了我所说的第一种情况的AdvancedDataGrid的执行效率。左边是使用了UIComponent,右边使用了第一段代码的方式。为了使差别更明显,我在每个Renderer里放置了两张图片,并且显示了3列,1万条数据,虽然左边的滚动条滑动速度也不是很流畅,但是相对于右边来说差别已经很明显了。右键查看源代码

http://www.flextheworld.com/demo/grid-itemrenderer/Test.html(0)

<mx:DataGridColumn dataField="atc" headerText="攻击" itemRenderer="OurItemRenderer" />

相关推荐