【转】理解itemRenderer
1. 理解itemRenderer
Flex中提供了大量用于数据呈现的组件,这其中,以List系列组件最为常见,使用也最频繁,比如ComBoBox、List、Tree、TileList、DataGrid等等。这些组件有一个共同的特性,那就是支持自定义itemRenderer,这个功能非常强大,能够帮助我们实现很多实用的功能.
项目渲染器(ItemRenderer)是ListBase类列表控件的重要属性,我们在平常工作中会经常自定义项目渲染器以实现该类控件的特殊显示效果。
默认的项目渲染器取决于组件类。TileList 和 HorizontalList 类使用 TileListItemRenderer;List 类使用 ListItemRenderer。DataGrid 类使用 DataGridColumn 中的 DataGridItemRenderer。
什么是itemRenderer?
itemRenderer,可以理解为数据的表现方式,具体地说,是指显示每一条数据时采用的形式。在使用数据处理类组件时,我们只需要把数据按照一定的格式组织好,然后赋予给组件,剩下的事就不用费心了。
每一个组件都有默认的itemRenderer,比如List组件:
List组件默认的itemRenderer类似Label组件,只简单显示一行文本。如右图所示,红色方框选中的是一个itemRenderer。
本质上,itemRenderer和其它容器类组件没有区别,里面可以包括任意的可视化元素,包括文本、图片、视频等,只要是Flex支持的视觉元素,都可以。
自定义itemRenderer
组件默认的itemRenderer都过于简单,如果想加强表现力,就必须创建个性化的itemRenderer。
自定义itemRenderer有两种方式:一种是嵌入式,直接写在组件的标签中,所有的代码都集中在一个MXML文件中;另一种是把itemRenderer独立出来,形成一个MXML组件或ActionScript类。
看下面图片,我用TileList控件的自定义ItemRenderer实现的效果:
代码下载:
https://files.cnblogs.com/aierong/Air_TestWin.rar
- <?xml version="1.0" encoding="utf-8"?>
- <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="145" height="41"
- horizontalScrollPolicy="off" verticalScrollPolicy="off">
- <mx:VBox width="40" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
- <mx:Image click="this.parentDocument.imgclick(event)"
- data="{data.label}" source="assets\tx.JPG" width="40"/>
- </mx:VBox>
- <mx:VBox width="95" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
- <mx:Label text="{data.label}" toolTip="{data.label}" />
- <mx:Image toolTip="点我查看视频" buttonMode="true"
- source="assets\camera.JPG" width="16" height="20" />
- </mx:VBox>
- </mx:HBox>
- <mx:ArrayCollection id="arrColl_keySetup">
- <mx:source>
- <mx:Array id="arr_keysetup">
- <mx:Object data="00" sel="false" label="aaa"/>
- <mx:Object data="11" sel="false" label="bbb"/>
- <mx:Object data="22" sel="true" label="ccc"/>
- </mx:Array>
- </mx:source>
- </mx:ArrayCollection>
- <mx:TileList id="tlist" maxColumns="1"
- dataProvider="{arrColl_keySetup}"
- itemRenderer="itemh"
- x="34" y="10" width="148">
- </mx:TileList>
给TileList的itemRenderer属性赋值itemh,并绑定数组集合类就可以了。
动态指定itemRenderer
利用ClassFactory类可以动态指定itemRenderer
- private function initTileList():void
- {
- this.tlist.dataProvider=this.arrColl_keySetup;
- this.tlist.itemRenderer = new ClassFactory(itemh);
- }
- <mx:TileList id="tlist" maxColumns="1" x="34" y="10" width="148"
- creationComplete="initTileList()">
- </mx:TileList>
至于怎样在项目渲染器访问主调用应用中的成员,可用parentDocument来引用;
反过来,主调用应用要访问项目渲染器中的成员,一般都是通过在项目渲染器先派发事件,然后在主调用应用中注册事件侦听器来实现。
例如:在项目渲染器中点击图片事件,然后通知主调用应用
<mx:Image click="this.parentDocument.imgclick(event)"
data="{data.label}" source="assets\tx.JPG" width="40"/>
在主调用应用中定义:
public function imgclick(evt:MouseEvent):void
{
var img:Image=evt.currentTarget as Image;
Alert.show(img.data.toString());
}
记住得是public的,不同类之间是无法访问私有方法的
图片点击后效果图片:
其实上面的功能也可以用dispatchEvent来实现
先定义一个事件类
- package
- {
- import flash.events.Event;
- public class myEvent extends Event
- {
- public var data:String;
- public function myEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false,data:String="")
- {
- super(type, bubbles, cancelable);
- this.data=data;
- }
- }
- }
然后在itemh中将事件调度到事件流中
- <mx:Image click="clickHandler(event)"
- data="{data.label}" source="assets\tx.JPG" width="40"/>
- private function clickHandler(evt:MouseEvent):void
- {
- var img:Image=evt.currentTarget as Image;
- dispatchEvent(new myEvent("img_click",true,true,img.data.toString()));
- }
最后在主调应用中监听事件
- private function initTileList():void
- {
- this.tlist.addEventListener("img_click",onItemimg_click);
- }
- private function onItemimg_click(evt:myEvent):void
- {
- Alert.show(evt.data);
- }
- <mx:TileList id="tlist" maxColumns="1" x="34" y="10" width="148"
- dataProvider="{arrColl_keySetup}" itemRenderer="itemh"
- creationComplete="initTileList()">
- </mx:TileList>
其实也可以用<mx:itemRenderer>和<mx:Component>标签来声明itemRenderer
例如以上代码可以改为:
- <mx:Script>
- <![CDATA[
- import mx.controls.Image;
- import mx.controls.Alert;
- public function imgclick(evt:MouseEvent):void
- {
- var img:Image=evt.currentTarget as Image;
- Alert.show(img.data.toString());
- }
- ]]>
- </mx:Script>
- <mx:ArrayCollection id="arrColl_keySetup">
- <mx:source>
- <mx:Array id="arr_keysetup">
- <mx:Object data="00" sel="false" label="aaa"/>
- <mx:Object data="11" sel="false" label="bbb"/>
- <mx:Object data="22" sel="true" label="ccc"/>
- </mx:Array>
- </mx:source>
- </mx:ArrayCollection>
- <mx:TileList id="tlist" maxColumns="1"
- dataProvider="{arrColl_keySetup}"
- x="34" y="10" width="148">
- <mx:itemRenderer>
- <mx:Component>
- <mx:HBox width="145" height="41" horizontalScrollPolicy="off" verticalScrollPolicy="off">
- <mx:VBox width="40" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
- <mx:Image click="this.parentDocument.imgclick(event)"
- data="{data.label}" buttonMode="true" source="assets\tx.JPG" width="40"/>
- </mx:VBox>
- <mx:VBox width="95" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
- <mx:Label text="{data.label}" toolTip="{data.label}"/>
- <mx:Image toolTip="点我查看视频" buttonMode="true"
- source="assets\camera.JPG" width="16" height="20"/>
- </mx:VBox>
- </mx:HBox>
- </mx:Component>
- </mx:itemRenderer>
- </mx:TileList>
(http://www.cnblogs.com/aierong/archive/2009/02/11/ItemRenderer.html )
itemRenderer的实现原理
itemRenderer的实现依赖于 AS3的一个重要特性:反射。反射 (Reflection) 是指在程序在运行时 (run-time) 获取类信息的方式,比如实现动态创建类实例、方法等。示例如下:
- import flash.utils.getQualifiedClassName;var cls:Class = flash.display.MovieClip;var mc:MovieClip = new cls();
- trace(getQualifiedClassName(mc));
- //输出:flash.display::MovieClip
itemRenderer的本质是Class类型,组件获取数据后,动态生成itemRenderer的实例。
itemRenderer 的特点
可以被当作 itemRenderer的组件,都实现了mx.core.IDataRenderer接口或mx.controls.listClasses.IListItemRenderer接口,还有mx.core.listClasses.IDropInListItemRenderer接口,用来实现有下拉框组件的itemRenderer。不管怎样,组件都有data属性,这是一个存取器性质的函数。
itemRenderer的工作方式
首先要明白一点,组件并不是很简单地把数据一次显示。为了提高运行效率,组件在处理数据时,只是创建了适当数量的itemRenderer,来完成显示区域的数据显示,这样,当数据源的数据量很大时,也不会耗费系统的资源。而且,itemRenderer是根据需要创建,创建后又是反复使用,很绿色,很环保。当显示区域发生变化或者拉动滚动条时,组件只是更新itemRenderer的数据。
比如,TileList、List组件,如果数据源只有一条数据,显示区域可以同时显示十条,则开始时只创建一个itemRenderer,添加一条数据,再创建一个新的itemRenderer。当数据量超过显示区域的最大值10时,就不再创建新的itemRenderer,而是回收利用现有资源。
另外,TileList、List还会创建一个多余的 itemRenderer,用来检测itemRenderer的尺寸等,方便定位。
虽然itemRenderer是真正的环保战士,但如果我们使用不当,就会出现很多奇怪的现象,比如内存泄露就是一个很常见的问题。在Adobe官方的论坛上,就经常见到这类讨论。其实,理解了itemRenderer的动作原理,这些问题就不难找到解决方法。
itemRenderer性能提高
对大批量数据的时候 DataGrid 中采用很多 itemRenderer ,性能将是一个大问题。于是寻找有关权威文章,期望能有所收获。在 Peter Ent 的 blog 上还是得到了不少帮助,原文是英文,也有人做过翻译,我这里就简单提取一部分仅供参考。需要详细,建议查看原文。
当我们在用 itemRenderer 显示大量数据的时候,无论是在 DataGrid 或者是 AdvanceDataGrid ( Flex3 ),如果我们的 itemRenderer 效率低下,那么整个系统的性能将大受影响。这里列出几个比较好的建议:
- 尽量少用 itemRenderer ,能不用的尽量别用。
- 不要频繁的更改 itemRenderer 的风格 change style 。也就是很多人喜欢的 setStyle(‘’,’’) ,比如更具数据变化背景颜色之类的,其实这个是很要命的,数据量小没什么感觉,数据量大了,效果就出来了。
- 尽量不要用容器 Cantainer 作为 itemRenderer 的基类。也就是我们常用的 HBox 和 VBox ,我的建议是宁愿用 Canvas 也不要用前者。至于原因讲起来就复杂一些。如果对高级 flex 开发人员来讲,我们应该尽量用 UIComponent ,作为基类,也就是所有界面元素的父类。
不用 setStyle 方法的替代法
这里有个很好的例子,就是根据所在列的数据来变更具体要显示的控件,以此来达到变换背景色的目的。
<mx:Canvas>
<mx:Script><![CDATA
private function lessThanZero() : Boolean {
return data.price < 0;
}
]]></mx:Script>
<mx:Label text="{data.price}" color="#FF0000" visible="{lessThanZero()}" />
<mx:Label text="{data.price}" color="#00FF00" visible="{!lessThanZero()}" />
</mx:Canvas>
这种方法可比 setStyle 来得快多了,但是在程序设计上稍显麻烦。
这里还需要注意的几点:
-
避免把风格和数据进行绑定,数据随时在变, style 也随时在变的话可以想想它的性能。
-
采用 Canvas 或者继承 ListItemRenderer 来作为 itemRenderer 的主容器,这样便于你自己对各个空间进行控制
另外如何采用 UIComponent 作为 itemRenderer 的容器基类,有兴趣的朋友可以参考原文 ,这里不做翻译和解释,原文其实讲的很明白了。(http://wmcai.blog.163.com/blog/static/48024200831833343734/ )
好例子:
http://blog.minidx.com/2008/10/03/1457.html
http://blog.minidx.com/2008/03/09/559.html
这里说的更好:
http://www.adobe.com/cn/devnet/flex/articles/itemrenderers_pt1.html
http://www.adobe.com/cn/devnet/flex/articles/itemrenderers_pt2.html