数据驱动组件是通过绑定机制监视数据源,通过渲染机制呈现数据的一类组件,数据驱动组件包括基本列表、高级列表、菜单和导航按钮组件,其共同特点是通过dataProvider:Object属性,采取绑定方式工作,Repeater虽然不能算作组件,但仍然采用数据驱动的思路创建重复组件,可以看出这种机制是应用的非常成功的。
dataProvider属性
对于Flex组件和容器来说,dateProvider属性与绑定有着特殊的渊源,dataProvider不仅是数据的来源而且还要进行渲染,是数据和显示分离的重要标志。列表要求数据源不仅能被监测还要便于呈现,为此dataProvier具有将低级对象转化为集合的能力,这些转化过程都在dataProvider的set方法中实现,而用于普通绑定的属性是不会做这些工作的。
自动转化规则
dataProvider的转化规则如下:
如果类型为 ICollectionView,则不执行任何转换。
如果类型为 IList,则转换为 ListCollectionView。
如果类型为 Array,则转换为 ArrayCollection。
如果类型为 XML 或 XMLList,则转换为 XMLListCollection。
在其他情况下,转换为单一元素 ArrayCollection。
转化规则是依据如下继承关系而来的:
上图要说明ICollectionView是可视集合所需的接口,ICollectionView接口定义过滤、排序、选取等视觉呈现所需能力,IList为集合添加数据操作接口,ListCollectionView是这两个接口的完整实现,是拓展集合的重要基石。
选择合适的集合类型
dataProvider最常用的集合类型除了扩展ListCollectionView的ArrayCollection和XMLListCollection还有Model,由于使用频繁,Flex提供如果了mxml定义方式,mxml语法大大简化了静态初始化集合的代码,当指定数据驱动组件创数据源时,应直接定义成这3种类型,避免直接操作低级对象带来的不明确性。
ArrayCollection
ArrayCollection非常适合线性数据,能够使用[]和for循环来保留使用Array的习惯,相比XMLListCollection和Model,ArrayCollection的元素没有类型限制。ArrayCollection描述树形结构有诸多的缺点,原因如下:
首先多维数组难以阅读和修改,ArrayCollection也是如此,再者ArrayCollection是唯一不修改元素的集合,而XMLLListCollection和ObjectProxy通过对元素动态增补来实现绑定,要进行多级绑定,需手动构建嵌套的ArrayCollection数组,没有多级转化Array的能力,也没有读取外部文件的能力,没有更好的理由,不要使用ArrayCollection描述数型结构。
XMLListCollection
XMLListCollection非常适合树型结构数据,因为XML对树形数据的操作具有天生的优势,且可以从外部文件或者服务器直接获取,经过XMLListCollection包装后的XML可以自动监测深层节点的变化,但不能使用XML操作符(使用IList定义的接口)。XMLListCollection的优点是可以直接定义子级,而ArrayCollection和Model需要使用children属性定义子级。
使用XMLListCollection描述线性数据并不具有优势,当dataProvider转化低级对象时,在mxml的绑定语法中,如果表达式中包含的XML需要明确的指出类型,否则会导致转化失败。例如将xml定义为Object将不能识别,运行时会弹出类似” warning: unable to bind to property 'city' on class 'XMLList'”的警告。另外更加严重的一个问题是,当XML中包含相同名称的节点时,Flex可能会向XML中加入标记节点<mx_internal_uid>来进行唯一标识,标记节点虽然在显示时会被过滤掉,但会影响我们再次进行节点搜索,而ArrayCollection是依靠索引来检索元素的,不存在节点名称相同的情况。
Model
Model可描述线性数据和树型数据,Model并不扩展ListViewCollection,因此Model具有体积小的优点,可以直接当作数据储存器使用,更难得的是mxml支持Model元素使用绑定,使得Model成为优秀的数据收集器。在绑定机制上Model能够自动进行深层绑定,还能从外部读取XML文件,拥有XMLListCollection的特点,但由于抛开了ListViewCollection这个基石,操作性上不如ArrayCollection和XMLListCollection全面,作为线性数组时仍需要通过children字段定义子级,相比之下Model更偏向于数据储存和信息收集。
ListBase
ListBase是基础列表组件的基石,考虑到列表组件广泛使用性,ListBase拥有众多的成员和极强的扩展能力。
列表大小与行和列显示
columnCount:int和rowCount:int设置显示的列和行数量,columnWidth:Number和rowHeight:Number设置列宽和行高。行列属性并非适用于所有列表,还受到自动换行的影响。
列宽、列数与列表宽度
columnCount:int
对于只有一列的List和Tree来说columnCount意义并不大,对于DataGrid列宽由DataGridColumn设置,columnCount适用于多列的TileList和HorizontalList的显示列数。 columnCount,columnWidth和列表的width属性并不互相制约,列表组件通过滑动条、剪裁和添加空白来维持各个值。
columnWidth:Number
对于多列的TileList和HorizontalList有两种方式决定列表大小,一种是通过设置columnWidth和columnCount,这种方式让内容显示比较精确。另一种是通过columnWidth和width属性让列表自动计算显示的行数,这种方式适用于对列表宽度有要求的情况,例如列表宽度适用百分比。如果同时设置了列宽、列数和列表宽度,那么columnCount会失效。
行高、行数与列表的高度
固定的行高
rowCount和rowHeight对于所有列表均有效,列表默认采用固定行高,不自动换行时,行高、行数与列表高度的设置方法与列相同。当设置固定行高时,超出显示范围的区域将被剪裁。
变化的行高
当variableRowHeight:Boolean属性为true时列表可以拥有不同的行高,不同的行高适用于两种情况,一是条目显示内容不一样时,例如有些条目包括图标和文字标签,有些条目只包括文字标签,拥有图标条目所需的行高要大与只有文字标签的条目,设置variableRowHeight为true后列表会自动适应每行的行高。另外一个用途就是自动换行,设置variableRowHeight为true和列宽后将wordWrap:Boolean为true可以启用自动换行,自动换行后列表根据文本内容设置行高。变化的行高打破了计算列表高度的方式,rowHeight和rowCount均会无效,只能设置height属性,列表自动确定行高和行数。
默认数值
列宽默认为50,列数默认为4,行高默认为20,行数,显示行数为7,rowHeight默认为20。如果不设置任何与行列相关的属性,列表将采取第一种方式计算其大小。
选择条目
要使列表的条目可以选择,需保证selected:Boolean属性为true,不过默认情况下列表都是可以选择条目的,selectedIndex:int和selectedItem:Object是广泛使用的条目选择属性且可以作为绑定源。设置selectedIndex和设置selectedItem的效果相同,如果条目为数值则selectedItem按数值比较,如果为引用则按引用比较。对于List组件selectedIndex默认为-1,selectedItem默认为null,ComboBox在聚合列表后将自身的selectedIndex默认为0,selectedItem为第一项。allowMultipleSelection:Boolean属性允许是开启多选功能,如果开启多选功能则selectedIndices:Array和selectedItems:Array记录选择的条目索引和选项数组。
ListBase提供一个改善选择条目体验的属性:allowDragSelection:Boolean,这个属性允许先按下鼠标再移动到条目,主要针对下拉列表,如ComboBox组件,如果你的自定义组件也需要做成下拉列表式,因该考虑到这个属性。
拖放条目
列表支持内部条目的拖放,拖放细则由各个列表子类实现,ListBase中定义启动拖放的属性,如下:
dragEnabled:Boolean 是否允许拖动条目
dropEnabled:Boolean 是否允许放置条目
dragMoveEnabled:Boolean 移动还是复制条目
详细内容请阅读拖放章节。
单元渲染器
单元渲染器将数据条目实例化为指定的视觉组件,单元渲染器由列表组件的itemRenderer:IFactory属性指定,Flex的内置列表有默认的单元渲染器,如下:
List ListItemRenderer
TileList TileListItemRenderer
HorizontalList TileListItemRenderer
DataGrid DataGridItemRenderer
Tree TreeItemRenderer
默认渲染器满足列表组件的基本需求,你通过将自定义渲染器来满足项目需求,单元渲染器只能有一个,不能单独为条目指定单元渲染器。
单元渲染器的渲染机制
列表组件的数据条目数量可能非常巨大,如果为每个数据条目创建一个单元渲染器实例会立即耗尽所有的内存,实际上并不需要为每一个数据条目创建单元渲染器,因为用户可以看到的数据条目是有限的,只要为可视的部分创建单元渲染器即可,列表组件在用户滚动到其它条目时也并非频繁的销毁和新建实例,而是对已有的单元渲染器循环利用,这一机制类似水车打水,如图:
实际上列表组件准备的单元渲染器要比可视数量多一些,多的单元渲染器用于性能优化,性能优化的方式使用奴隶与死囚优化法则,创建新的渲染器时如果缓存中有则从缓存中去,如果没有创建新的渲染器。单元渲染器在性能上明显高于容器+Repeater组件,但多了换算的步骤,为了顺利查找数据条目和单元渲染器对应的关系,ListBase提供了全面的转化方法:
通过单元渲染器获取数据条目
在获取单元渲染器引用的情况下,可以直接通过data属性获取数据条目,无需调用ListBase的方法。
通过单元渲染器获取条目索引
itemRendererToIndex(itemRenderer:IListItemRenderer):int提供单元渲染器到条目索引的转换,对于Tree组件返回显示索引。
通过条目索引获取单元渲染器
反过来,如果知道数据条目索引,可以通过indexToItemRenderer(index:int):IListItemRenderer进行转换,注意只能查找已被实例化的单元渲染器,即条目索引必须被载入单元渲染器。要判断单元渲染器是否装载某条数据,可以调用isItemVisible(item:Object):Boolean方法。对于Tree组件,index为显示索引。
通过数据条目获取单元渲染器
itemToItemRenderer(item:Object):IListItemRenderer是通过数据条目查找单元渲染器的方法,同indexToItemRenderer()方法一样要求条目已被载入到单元渲染器。
选取单元渲染器
了解了单元渲染器的渲染机制后,我们知道了数据和单元渲染器之间的关系和转化方法,此时通过鼠标选取单元渲染器是件容器的事情。ListBase的selectedIndex和selectedItem属性提供了选取条目索引和条目数据的能力,但不直接提供选取单元渲染器的属性,要查找当前选择条目的单元渲染器,应该在change事件中获取,ListEvent提供单元渲染器的引用。当然你也可以通过indexToItemRenderer()和itemToItemRenderer()方法将索引或数据条目进行转化,找到数据条目后可以通过isItemSelectable(data:Object):Boolean和isItemSelected(data:Object):Boolean方法判断单元渲染器是否可选和是否选中,通过isItemHighlighted(data:Object):Boolean方法判断单元渲染器是否被加亮。
在单元渲染器中编写代码
在单元渲染器中编写代码比起在列表事件中编写代码具有优势,单元渲染器内部能够直接通过data属性获取条目数据,通过listData获取单元信息,避开了转化过程且符合封装的需求。当滚动列表时,单元渲染器会不断的更新数据,频繁触发dataChange事件,如果包含修改视觉外观的代码要同时考虑到普通外观和修改后的外观更新。
单元渲染器的数据传递机制
单元渲染器数据传递过程是列表组件设置单元渲染器特定属性的过程,这些属性通过各类接口制定,相关的接口包括:IListItemRenderer, IDataRenderer, IDropInListItemRenderer,下面我们看看这些接口意义及实现内幕。
通过data属性传递数据条目
所有的列表都是通过设置单元渲染器的data:Object属性传递数据条目的,data属性是IDataRenderer接口定义的,所有单元渲染器都必须实现此接口。data通常定义为get/set属性,在data的set方法可以加入逻辑代码。通常有3中方式分析和设置组件自身属性来更改其视觉呈现:
在data的set方法中添加逻辑代码
在dataChange事件中添加,设置data后会触发dataChange事件
使用绑定
绑定是最简单直接的做法,但是绑定不能加入代码逻辑,在set方法中编写逻辑是标准的方法,但意味着要覆盖基类属性,使用dataChange事件是一种被动的做法,当你不想覆盖data属性时是很好的选择。
通过listData:BaseListData属性传递单元信息
对于单元渲染器,只知道需要呈现的数据条目是不够的,还需要知道条目所处的单元信息,例如知道单元渲染器所在的行和列后才能明确当前数据条目处于数据表格的哪一行哪一列,知道显示的文本和图标后才能创建相关的对象,单元渲染器需要这些信息进行渲染。单元信息记录在单元渲染器的listData:BaseListData属性中,被IDropInListItemRenderer接口所定义,listData是通过单元渲染器获取数据条目和渲染信息的重要方式。
不同的列表组件会将不同的单元信息传递给单元渲染器,对于List,HorizontalList,TileList类型为ListData,对于DataGrid类型为 DataGridListData,对于Tree类型为 TreeListData,对于Menu类型为MenuListData,BaseListData是这些类型的基类,记录的信息包括:
owner:IUIComponent 所属的列表
rowIndex:int 单元渲染器所在的行
columnIndex:int 单元渲染器所在的列
label:String 单元渲染器标签
uid:String 唯一标识
子类会记录一些额外的信息,例如ListData使用labelField:String和iconClass:Class属性记录字段名和图标类,TreeListData记录了节点的层级、打开状态等,如果没有这些信息,就只能通过数据条目进行硬编码,例如在DataGrid中匹配studentName字段的单元渲染器不能用作其它列,因为单元渲染器不知道处于哪一列,知道单元信息后,就可以将多列设置为同一个单元渲染器,根据所在列数来显示,实现IDropInListItemRenderer接口的单元渲染器又称为“嵌入式”单元渲染器。listData总是和data属性同步更新并在data之前设置,但listData不会触发dataChange事件。在具有IDropInListItemRenderer接口的组件中,data经常需要访问listData中的数据,在自定义渲染器中,如果有必要也可以覆盖listData属性来影响data中的取值。
IListItemRendener接口
IListItemRenderer接口是作为单元渲染器的基本要求,IListItemRenderer自身并不定义方法,但它继承了IDataRenderer,IFlexDisplayObject,ILayoutManagerClient,ISimpleStyleClient和IUIComponent接口,IListItemRenderer接口向我们表明单元渲染器至少是一个拥有data属性的UIComponent组件,如果要定义嵌入式单元渲染器,还必须实现IDropInListItemRenderer接口。换句话说,单元渲染器必须有data属性,但不一定要有listData属性,列表组件发现单元渲染器不是IDropInListItemRenderer会略过listData的设置。
使用默认渲染器
默认渲染器是实现IListItemRenderer和IDropInListItemRenderer的UIComponent组件,列表组件的默认渲染器有很多共同的地方,它们显示标签、图标和提示文本中的一种或几种,默认渲染器读取listData中的单元信息,列表组件通过自身属性匹配数据源中的字段写入listData,如下表:
属性 默认值 处理函数 默认值 说明
labelField:String label labelFunction:Function null 文本标签
iconField:String null iconFunction:Function null 图标
dataTipField:String label dataTipFunction:Function null 提示文本
字段属性提供默认值,这表示使用能够定义数据源的情况下将数据源定义为默认的字段名可以省去字段属性的设置,但数据源来自于第三方时必须进行设置,默认渲染器在字段为空时不会显示具体内容,例如iconField为null时不会显示图标。处理函数用于对字段进行加工,例如进行字段组合显示或格式化。
将Flex组件作为渲染器
能够作为渲染器的内置Flex组件有Button,ComboBox,DateField,Image,Label,NumericStepper,TextArea,TextInput,它们都实现IListItemRenderer和IDropInListItemRenderer接口,能够直接使用。使用Flex组件时,如果<script>标签中没有导入该组件则需要使用全路径,因为在实例化渲染器时Flex需要明确知道其路径。下面例子使用NumericStepper作为单元渲染器:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:ArrayCollection id="myArr">
[1,2,3]
</mx:ArrayCollection>
<mx:List id="lt" dataProvider="{myArr}" itemRenderer="mx.controls.NumericStepper" width="84" x="278" y="114"/>
<mx:Label />
</mx:Application>
使用内置Flex组件作为单元渲染时,设置data属性有默认行为,例如Image会尝试将单元信息的label字段或整个数据条目作为source属性用,NumericStepper将整个数据条目写入value属性。
使用容器作为渲染器
Container实现IListItemRenderer接口但不实现IDropInListItemRenderer接口,将纯容器作为单元渲染器没有什么意义,容器通常用于创建复合组件的单元渲染器。使用容器作为单元渲染器时,data属性是容器的属性,要想访问单元信息,记得实现IDropInListItemRenderer接口。
自定义渲染器
单一内置组件显得过于简单,默认渲染器往往不能满足项目需求,我们需要扩展内置组件或创建复合组件来解决问题。如果你想使用硬编码,则无需设置列表的labelField,iconField等属性设置,也无需实现IDropInListItemRenderer接口,下例创建一个硬编码的复合组件作为单元渲染器,解决默认的渲染器ListItemRenderer图标不能读取外部文件的问题:
MyListItemRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Image id="headIcon" source="{data.url}" />
<mx:Label id="iconLabel" text="{data.label}" />
</mx:HBox>
FlexTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function listCreationComplete():void
{
var iconList:Array=[];
var i:int=0;
for(i=0;i<21;i++)
{
iconList.push({label:"卡通图标"+i,url:"headIcon/cartoon/cartoon"+i+".png"});
}
myArr.source=iconList;
}
]]>
</mx:Script>
<mx:ArrayCollection id="myArr" />
<mx:List id="lt" dataProvider="{myArr}" itemRenderer="MyListItemRenderer" creationComplete="listCreationComplete()" rowHeight="45"/>
</mx:Application>
硬编码非常适合绑定,因为已经制定好了规范,且单元渲染器不会用于其它列表。如果要制作嵌入式渲染器或读取单元信息,就需要从listData中读取单元信息了:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" implements="mx.controls.listClasses.IDropInListItemRenderer" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Script>
<![CDATA[
import mx.controls.menuClasses.MenuListData;
import mx.controls.treeClasses.TreeListData;
import mx.controls.HorizontalList;
import mx.controls.listClasses.ListData;
import mx.controls.listClasses.BaseListData;
private var _listData:BaseListData;
public function get listData():BaseListData
{
return _listData;
}
public function set listData(value:BaseListData):void
{
_listData=value;
}
override public function set data(value:Object):void
{
if (listData.label)
{
iconLabel.text=value.label;
}
if (listData is ListData || listData is TreeListData || listData is MenuListData)
{
headIcon.source=value.url;
}
}
]]>
</mx:Script>
<mx:Image id="headIcon"/>
<mx:Label id="iconLabel"/>
</mx:HBox>
在写入listData属性时,将调用ListBase的itemToLabel(data:Object):String、itemToIcon(data:Object):Class、itemToDataTip(data:Object):String进行计算,这里通过itemToIcon()方法计算的ListData的icon信息仍然是个图标资源类,而url字段是图片地址,因此不能将地址写入信息单元,这是ListData所局限的,因为ListData更倾向为默认的ListItemRenderer服务。
使用内联方式创建
单元渲染器可以使用内联方式定义,itemRenderer的类型为IFactory,实现IFactory接口的类主要有ClassFactory,在mxml中如果itemRenderer类型为IFactory将调用newInstance()方法创建实例,如果指定为组件类编译器会自动使用IFactory进行包装,因此不能使用代码方式将组件类指定给itemRenderer属性。使用mxml创建内联组件的方式是使用<mx:Component>标签,<Component>被编译成ClassFactory类,其中的组件被编译到独立的类中,现在我们使用内联方式将MyListItemRenderer修改如下:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function listCreationComplete():void
{
var iconList:Array=[];
var i:int=0;
for(i=0; i < 21; i++)
{
iconList.push({label:"卡通图标" + i, url:"headIcon/cartoon/cartoon" + i + ".png"});
}
myArr.source=iconList;
}
]]>
</mx:Script>
<mx:ArrayCollection id="myArr"/>
<mx:List id="lt" dataProvider="{myArr}" creationComplete="listCreationComplete()" rowHeight="45">
<mx:itemRenderer>
<mx:Component>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Image id="headIcon" source="{data.url}"/>
<mx:Label id="iconLabel" text="{data.label}"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
</mx:Application>
由于内联中的组件被编译成独立的类文件,因此内联不同于包外类,内联带来了两个编程体验:
内联将单元渲染器和列表代码写在同一个mxml中,代码更加集中,当我们扩展列表组件并自定义渲染器时,可以体会到包外类方式的mxml创建方式。
内联所处的运行环境是独立的,但可以使用outerDocument属性访问包含它的mxml文件实例,比较便捷,outerDocument在创建内联时由Flex自动生成。
内联虽然访问父级的成员,但对封装性和垃圾回收并没有太大的影响,因为采取内联方式扩展的列表组件和自定义单元渲染器常常是一一对应关系。单元渲染器只是单方面引用所在的mxml中的成员,而外部并没有单独对单元渲染器进行引用,单元渲染器将和列表一并删除,不会造成内存溢出。
扩展默认渲染器
默认渲染器是重新创建自定义渲染器的经典例子,但扩展默认渲染器并不是很好的选择方式,默认渲染器大多已经定型且非容器,因此可扩展性不强,修改默认渲染器通常用于在此基础上增强视觉外观,例如:
<?xml version="1.0" encoding="utf-8"?>
<ListItemRenderer xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="mx.controls.listClasses.*" rollOver="changeFontSize()"
rollOut="restoreFontSize()" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
private var orginalFontSize:int;
private function changeFontSize():void
{
setStyle("fontSize",orginalFontSize+5);
}
private function restoreFontSize():void
{
setStyle("fontSize",orginalFontSize);
}
private function onCreationComplete():void
{
orginalFontSize=getStyle("fontSize") as int;
}
]]>
</mx:Script>
</ListItemRenderer>
空条目渲染器
当数据条目是一个稀疏数组时,可以指定空条目的渲染器,例如显示一张空缺的位图,空条目渲染器由ListBase的nullItemRenderer:Object属性定义,当条目为空且指定nullItemRenderer时,列表尝试创建空条目渲染器的实例。Flex不提供默认的空条目渲染器,如果不指定空条目渲染器则依然使用单元渲染器。
滚动列表
除了使用鼠标和键盘滚动列表外,可以调用scrollToIndex(index:int):Boolean方法滚动列表,index为需要显示在最上层的条目。如果条目已经可见则不会有任何变化。ListBase继承ScrollControlBase类,因此滚动能力是非常全面的,关于ScrollControlBase,请查看滚动自定义组件章节。
查找数据条目
虽然查找条目的通常做法是先通过数据源找到条目索引然后再设置selectedIndex或selectedItem属性,但ListBase还是提供了一个findString(str:String):Boolean的简单方法,findString通常查找条目的label属性和条目本身,不能设置细节的匹配规则,适用于简单的数据列表。
条目编辑器
不一定所有的列表都有条目编辑器,具有条目编辑器的列表在自身中定义条目编辑器,条目编辑器在设计上也同单元渲染器不同,详细情况将在子类中讲述。
列表事件
列表事件为ListEvent,ListEvent大多针对用户与单元渲染器的互动,ListEvent记录如下属性:
itemRenderer:IListItemRenderer 单元渲染器
rowIndex:int 条目行索引
columnIndex:int 条目列索引
reson:String 调度itemEditEnd事件的原因
其中最重要的是itemRenderer属性,它是通过鼠标操作获取单元渲染器引用的主要手段。rowIndex和columnIndex是条目索引,不是单元渲染器的信息记录中的位置,reson只对条目编辑器有意义。
ListBase为列表提供基本事件如下:
事件 描述
change selectedIndex和selectedItem改变时触发
itemClick 单击条目触发,即使未选中条目也会触发
itemDoubleClick 双击条目触发,需要将列表的doubleClickEnabled设置为true,无需设置单元渲染器的doubleClickEnabled属性,UIComponent在设置doubleClickEnabled时会使用递归设置所有子级的doubleClickEnabled属性。
itemRollOver 鼠标掠过单元渲染器
itemRollOut 鼠标掠出单元渲染器
点击和鼠标掠过事件在单元渲染器内部也可以侦听,这里的鼠标事件对象是列表组件自身,主要用于数据操作时的定位,例如拖放数据时进行判断,而视觉效果的代码仍然建议在单元渲染器的鼠标事件中完成。
dataProvider属性
对于Flex组件和容器来说,dateProvider属性与绑定有着特殊的渊源,dataProvider不仅是数据的来源而且还要进行渲染,是数据和显示分离的重要标志。列表要求数据源不仅能被监测还要便于呈现,为此dataProvier具有将低级对象转化为集合的能力,这些转化过程都在dataProvider的set方法中实现,而用于普通绑定的属性是不会做这些工作的。
自动转化规则
dataProvider的转化规则如下:
如果类型为 ICollectionView,则不执行任何转换。
如果类型为 IList,则转换为 ListCollectionView。
如果类型为 Array,则转换为 ArrayCollection。
如果类型为 XML 或 XMLList,则转换为 XMLListCollection。
在其他情况下,转换为单一元素 ArrayCollection。
转化规则是依据如下继承关系而来的:
上图要说明ICollectionView是可视集合所需的接口,ICollectionView接口定义过滤、排序、选取等视觉呈现所需能力,IList为集合添加数据操作接口,ListCollectionView是这两个接口的完整实现,是拓展集合的重要基石。
选择合适的集合类型
dataProvider最常用的集合类型除了扩展ListCollectionView的ArrayCollection和XMLListCollection还有Model,由于使用频繁,Flex提供如果了mxml定义方式,mxml语法大大简化了静态初始化集合的代码,当指定数据驱动组件创数据源时,应直接定义成这3种类型,避免直接操作低级对象带来的不明确性。
ArrayCollection
ArrayCollection非常适合线性数据,能够使用[]和for循环来保留使用Array的习惯,相比XMLListCollection和Model,ArrayCollection的元素没有类型限制。ArrayCollection描述树形结构有诸多的缺点,原因如下:
首先多维数组难以阅读和修改,ArrayCollection也是如此,再者ArrayCollection是唯一不修改元素的集合,而XMLLListCollection和ObjectProxy通过对元素动态增补来实现绑定,要进行多级绑定,需手动构建嵌套的ArrayCollection数组,没有多级转化Array的能力,也没有读取外部文件的能力,没有更好的理由,不要使用ArrayCollection描述数型结构。
XMLListCollection
XMLListCollection非常适合树型结构数据,因为XML对树形数据的操作具有天生的优势,且可以从外部文件或者服务器直接获取,经过XMLListCollection包装后的XML可以自动监测深层节点的变化,但不能使用XML操作符(使用IList定义的接口)。XMLListCollection的优点是可以直接定义子级,而ArrayCollection和Model需要使用children属性定义子级。
使用XMLListCollection描述线性数据并不具有优势,当dataProvider转化低级对象时,在mxml的绑定语法中,如果表达式中包含的XML需要明确的指出类型,否则会导致转化失败。例如将xml定义为Object将不能识别,运行时会弹出类似” warning: unable to bind to property 'city' on class 'XMLList'”的警告。另外更加严重的一个问题是,当XML中包含相同名称的节点时,Flex可能会向XML中加入标记节点<mx_internal_uid>来进行唯一标识,标记节点虽然在显示时会被过滤掉,但会影响我们再次进行节点搜索,而ArrayCollection是依靠索引来检索元素的,不存在节点名称相同的情况。
Model
Model可描述线性数据和树型数据,Model并不扩展ListViewCollection,因此Model具有体积小的优点,可以直接当作数据储存器使用,更难得的是mxml支持Model元素使用绑定,使得Model成为优秀的数据收集器。在绑定机制上Model能够自动进行深层绑定,还能从外部读取XML文件,拥有XMLListCollection的特点,但由于抛开了ListViewCollection这个基石,操作性上不如ArrayCollection和XMLListCollection全面,作为线性数组时仍需要通过children字段定义子级,相比之下Model更偏向于数据储存和信息收集。
ListBase
ListBase是基础列表组件的基石,考虑到列表组件广泛使用性,ListBase拥有众多的成员和极强的扩展能力。
列表大小与行和列显示
columnCount:int和rowCount:int设置显示的列和行数量,columnWidth:Number和rowHeight:Number设置列宽和行高。行列属性并非适用于所有列表,还受到自动换行的影响。
列宽、列数与列表宽度
columnCount:int
对于只有一列的List和Tree来说columnCount意义并不大,对于DataGrid列宽由DataGridColumn设置,columnCount适用于多列的TileList和HorizontalList的显示列数。 columnCount,columnWidth和列表的width属性并不互相制约,列表组件通过滑动条、剪裁和添加空白来维持各个值。
columnWidth:Number
对于多列的TileList和HorizontalList有两种方式决定列表大小,一种是通过设置columnWidth和columnCount,这种方式让内容显示比较精确。另一种是通过columnWidth和width属性让列表自动计算显示的行数,这种方式适用于对列表宽度有要求的情况,例如列表宽度适用百分比。如果同时设置了列宽、列数和列表宽度,那么columnCount会失效。
行高、行数与列表的高度
固定的行高
rowCount和rowHeight对于所有列表均有效,列表默认采用固定行高,不自动换行时,行高、行数与列表高度的设置方法与列相同。当设置固定行高时,超出显示范围的区域将被剪裁。
变化的行高
当variableRowHeight:Boolean属性为true时列表可以拥有不同的行高,不同的行高适用于两种情况,一是条目显示内容不一样时,例如有些条目包括图标和文字标签,有些条目只包括文字标签,拥有图标条目所需的行高要大与只有文字标签的条目,设置variableRowHeight为true后列表会自动适应每行的行高。另外一个用途就是自动换行,设置variableRowHeight为true和列宽后将wordWrap:Boolean为true可以启用自动换行,自动换行后列表根据文本内容设置行高。变化的行高打破了计算列表高度的方式,rowHeight和rowCount均会无效,只能设置height属性,列表自动确定行高和行数。
默认数值
列宽默认为50,列数默认为4,行高默认为20,行数,显示行数为7,rowHeight默认为20。如果不设置任何与行列相关的属性,列表将采取第一种方式计算其大小。
选择条目
要使列表的条目可以选择,需保证selected:Boolean属性为true,不过默认情况下列表都是可以选择条目的,selectedIndex:int和selectedItem:Object是广泛使用的条目选择属性且可以作为绑定源。设置selectedIndex和设置selectedItem的效果相同,如果条目为数值则selectedItem按数值比较,如果为引用则按引用比较。对于List组件selectedIndex默认为-1,selectedItem默认为null,ComboBox在聚合列表后将自身的selectedIndex默认为0,selectedItem为第一项。allowMultipleSelection:Boolean属性允许是开启多选功能,如果开启多选功能则selectedIndices:Array和selectedItems:Array记录选择的条目索引和选项数组。
ListBase提供一个改善选择条目体验的属性:allowDragSelection:Boolean,这个属性允许先按下鼠标再移动到条目,主要针对下拉列表,如ComboBox组件,如果你的自定义组件也需要做成下拉列表式,因该考虑到这个属性。
拖放条目
列表支持内部条目的拖放,拖放细则由各个列表子类实现,ListBase中定义启动拖放的属性,如下:
dragEnabled:Boolean 是否允许拖动条目
dropEnabled:Boolean 是否允许放置条目
dragMoveEnabled:Boolean 移动还是复制条目
详细内容请阅读拖放章节。
单元渲染器
单元渲染器将数据条目实例化为指定的视觉组件,单元渲染器由列表组件的itemRenderer:IFactory属性指定,Flex的内置列表有默认的单元渲染器,如下:
List ListItemRenderer
TileList TileListItemRenderer
HorizontalList TileListItemRenderer
DataGrid DataGridItemRenderer
Tree TreeItemRenderer
默认渲染器满足列表组件的基本需求,你通过将自定义渲染器来满足项目需求,单元渲染器只能有一个,不能单独为条目指定单元渲染器。
单元渲染器的渲染机制
列表组件的数据条目数量可能非常巨大,如果为每个数据条目创建一个单元渲染器实例会立即耗尽所有的内存,实际上并不需要为每一个数据条目创建单元渲染器,因为用户可以看到的数据条目是有限的,只要为可视的部分创建单元渲染器即可,列表组件在用户滚动到其它条目时也并非频繁的销毁和新建实例,而是对已有的单元渲染器循环利用,这一机制类似水车打水,如图:
实际上列表组件准备的单元渲染器要比可视数量多一些,多的单元渲染器用于性能优化,性能优化的方式使用奴隶与死囚优化法则,创建新的渲染器时如果缓存中有则从缓存中去,如果没有创建新的渲染器。单元渲染器在性能上明显高于容器+Repeater组件,但多了换算的步骤,为了顺利查找数据条目和单元渲染器对应的关系,ListBase提供了全面的转化方法:
通过单元渲染器获取数据条目
在获取单元渲染器引用的情况下,可以直接通过data属性获取数据条目,无需调用ListBase的方法。
通过单元渲染器获取条目索引
itemRendererToIndex(itemRenderer:IListItemRenderer):int提供单元渲染器到条目索引的转换,对于Tree组件返回显示索引。
通过条目索引获取单元渲染器
反过来,如果知道数据条目索引,可以通过indexToItemRenderer(index:int):IListItemRenderer进行转换,注意只能查找已被实例化的单元渲染器,即条目索引必须被载入单元渲染器。要判断单元渲染器是否装载某条数据,可以调用isItemVisible(item:Object):Boolean方法。对于Tree组件,index为显示索引。
通过数据条目获取单元渲染器
itemToItemRenderer(item:Object):IListItemRenderer是通过数据条目查找单元渲染器的方法,同indexToItemRenderer()方法一样要求条目已被载入到单元渲染器。
选取单元渲染器
了解了单元渲染器的渲染机制后,我们知道了数据和单元渲染器之间的关系和转化方法,此时通过鼠标选取单元渲染器是件容器的事情。ListBase的selectedIndex和selectedItem属性提供了选取条目索引和条目数据的能力,但不直接提供选取单元渲染器的属性,要查找当前选择条目的单元渲染器,应该在change事件中获取,ListEvent提供单元渲染器的引用。当然你也可以通过indexToItemRenderer()和itemToItemRenderer()方法将索引或数据条目进行转化,找到数据条目后可以通过isItemSelectable(data:Object):Boolean和isItemSelected(data:Object):Boolean方法判断单元渲染器是否可选和是否选中,通过isItemHighlighted(data:Object):Boolean方法判断单元渲染器是否被加亮。
在单元渲染器中编写代码
在单元渲染器中编写代码比起在列表事件中编写代码具有优势,单元渲染器内部能够直接通过data属性获取条目数据,通过listData获取单元信息,避开了转化过程且符合封装的需求。当滚动列表时,单元渲染器会不断的更新数据,频繁触发dataChange事件,如果包含修改视觉外观的代码要同时考虑到普通外观和修改后的外观更新。
单元渲染器的数据传递机制
单元渲染器数据传递过程是列表组件设置单元渲染器特定属性的过程,这些属性通过各类接口制定,相关的接口包括:IListItemRenderer, IDataRenderer, IDropInListItemRenderer,下面我们看看这些接口意义及实现内幕。
通过data属性传递数据条目
所有的列表都是通过设置单元渲染器的data:Object属性传递数据条目的,data属性是IDataRenderer接口定义的,所有单元渲染器都必须实现此接口。data通常定义为get/set属性,在data的set方法可以加入逻辑代码。通常有3中方式分析和设置组件自身属性来更改其视觉呈现:
在data的set方法中添加逻辑代码
在dataChange事件中添加,设置data后会触发dataChange事件
使用绑定
绑定是最简单直接的做法,但是绑定不能加入代码逻辑,在set方法中编写逻辑是标准的方法,但意味着要覆盖基类属性,使用dataChange事件是一种被动的做法,当你不想覆盖data属性时是很好的选择。
通过listData:BaseListData属性传递单元信息
对于单元渲染器,只知道需要呈现的数据条目是不够的,还需要知道条目所处的单元信息,例如知道单元渲染器所在的行和列后才能明确当前数据条目处于数据表格的哪一行哪一列,知道显示的文本和图标后才能创建相关的对象,单元渲染器需要这些信息进行渲染。单元信息记录在单元渲染器的listData:BaseListData属性中,被IDropInListItemRenderer接口所定义,listData是通过单元渲染器获取数据条目和渲染信息的重要方式。
不同的列表组件会将不同的单元信息传递给单元渲染器,对于List,HorizontalList,TileList类型为ListData,对于DataGrid类型为 DataGridListData,对于Tree类型为 TreeListData,对于Menu类型为MenuListData,BaseListData是这些类型的基类,记录的信息包括:
owner:IUIComponent 所属的列表
rowIndex:int 单元渲染器所在的行
columnIndex:int 单元渲染器所在的列
label:String 单元渲染器标签
uid:String 唯一标识
子类会记录一些额外的信息,例如ListData使用labelField:String和iconClass:Class属性记录字段名和图标类,TreeListData记录了节点的层级、打开状态等,如果没有这些信息,就只能通过数据条目进行硬编码,例如在DataGrid中匹配studentName字段的单元渲染器不能用作其它列,因为单元渲染器不知道处于哪一列,知道单元信息后,就可以将多列设置为同一个单元渲染器,根据所在列数来显示,实现IDropInListItemRenderer接口的单元渲染器又称为“嵌入式”单元渲染器。listData总是和data属性同步更新并在data之前设置,但listData不会触发dataChange事件。在具有IDropInListItemRenderer接口的组件中,data经常需要访问listData中的数据,在自定义渲染器中,如果有必要也可以覆盖listData属性来影响data中的取值。
IListItemRendener接口
IListItemRenderer接口是作为单元渲染器的基本要求,IListItemRenderer自身并不定义方法,但它继承了IDataRenderer,IFlexDisplayObject,ILayoutManagerClient,ISimpleStyleClient和IUIComponent接口,IListItemRenderer接口向我们表明单元渲染器至少是一个拥有data属性的UIComponent组件,如果要定义嵌入式单元渲染器,还必须实现IDropInListItemRenderer接口。换句话说,单元渲染器必须有data属性,但不一定要有listData属性,列表组件发现单元渲染器不是IDropInListItemRenderer会略过listData的设置。
使用默认渲染器
默认渲染器是实现IListItemRenderer和IDropInListItemRenderer的UIComponent组件,列表组件的默认渲染器有很多共同的地方,它们显示标签、图标和提示文本中的一种或几种,默认渲染器读取listData中的单元信息,列表组件通过自身属性匹配数据源中的字段写入listData,如下表:
属性 默认值 处理函数 默认值 说明
labelField:String label labelFunction:Function null 文本标签
iconField:String null iconFunction:Function null 图标
dataTipField:String label dataTipFunction:Function null 提示文本
字段属性提供默认值,这表示使用能够定义数据源的情况下将数据源定义为默认的字段名可以省去字段属性的设置,但数据源来自于第三方时必须进行设置,默认渲染器在字段为空时不会显示具体内容,例如iconField为null时不会显示图标。处理函数用于对字段进行加工,例如进行字段组合显示或格式化。
将Flex组件作为渲染器
能够作为渲染器的内置Flex组件有Button,ComboBox,DateField,Image,Label,NumericStepper,TextArea,TextInput,它们都实现IListItemRenderer和IDropInListItemRenderer接口,能够直接使用。使用Flex组件时,如果<script>标签中没有导入该组件则需要使用全路径,因为在实例化渲染器时Flex需要明确知道其路径。下面例子使用NumericStepper作为单元渲染器:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:ArrayCollection id="myArr">
[1,2,3]
</mx:ArrayCollection>
<mx:List id="lt" dataProvider="{myArr}" itemRenderer="mx.controls.NumericStepper" width="84" x="278" y="114"/>
<mx:Label />
</mx:Application>
使用内置Flex组件作为单元渲染时,设置data属性有默认行为,例如Image会尝试将单元信息的label字段或整个数据条目作为source属性用,NumericStepper将整个数据条目写入value属性。
使用容器作为渲染器
Container实现IListItemRenderer接口但不实现IDropInListItemRenderer接口,将纯容器作为单元渲染器没有什么意义,容器通常用于创建复合组件的单元渲染器。使用容器作为单元渲染器时,data属性是容器的属性,要想访问单元信息,记得实现IDropInListItemRenderer接口。
自定义渲染器
单一内置组件显得过于简单,默认渲染器往往不能满足项目需求,我们需要扩展内置组件或创建复合组件来解决问题。如果你想使用硬编码,则无需设置列表的labelField,iconField等属性设置,也无需实现IDropInListItemRenderer接口,下例创建一个硬编码的复合组件作为单元渲染器,解决默认的渲染器ListItemRenderer图标不能读取外部文件的问题:
MyListItemRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Image id="headIcon" source="{data.url}" />
<mx:Label id="iconLabel" text="{data.label}" />
</mx:HBox>
FlexTest.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function listCreationComplete():void
{
var iconList:Array=[];
var i:int=0;
for(i=0;i<21;i++)
{
iconList.push({label:"卡通图标"+i,url:"headIcon/cartoon/cartoon"+i+".png"});
}
myArr.source=iconList;
}
]]>
</mx:Script>
<mx:ArrayCollection id="myArr" />
<mx:List id="lt" dataProvider="{myArr}" itemRenderer="MyListItemRenderer" creationComplete="listCreationComplete()" rowHeight="45"/>
</mx:Application>
硬编码非常适合绑定,因为已经制定好了规范,且单元渲染器不会用于其它列表。如果要制作嵌入式渲染器或读取单元信息,就需要从listData中读取单元信息了:
<?xml version="1.0" encoding="utf-8"?>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" implements="mx.controls.listClasses.IDropInListItemRenderer" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Script>
<![CDATA[
import mx.controls.menuClasses.MenuListData;
import mx.controls.treeClasses.TreeListData;
import mx.controls.HorizontalList;
import mx.controls.listClasses.ListData;
import mx.controls.listClasses.BaseListData;
private var _listData:BaseListData;
public function get listData():BaseListData
{
return _listData;
}
public function set listData(value:BaseListData):void
{
_listData=value;
}
override public function set data(value:Object):void
{
if (listData.label)
{
iconLabel.text=value.label;
}
if (listData is ListData || listData is TreeListData || listData is MenuListData)
{
headIcon.source=value.url;
}
}
]]>
</mx:Script>
<mx:Image id="headIcon"/>
<mx:Label id="iconLabel"/>
</mx:HBox>
在写入listData属性时,将调用ListBase的itemToLabel(data:Object):String、itemToIcon(data:Object):Class、itemToDataTip(data:Object):String进行计算,这里通过itemToIcon()方法计算的ListData的icon信息仍然是个图标资源类,而url字段是图片地址,因此不能将地址写入信息单元,这是ListData所局限的,因为ListData更倾向为默认的ListItemRenderer服务。
使用内联方式创建
单元渲染器可以使用内联方式定义,itemRenderer的类型为IFactory,实现IFactory接口的类主要有ClassFactory,在mxml中如果itemRenderer类型为IFactory将调用newInstance()方法创建实例,如果指定为组件类编译器会自动使用IFactory进行包装,因此不能使用代码方式将组件类指定给itemRenderer属性。使用mxml创建内联组件的方式是使用<mx:Component>标签,<Component>被编译成ClassFactory类,其中的组件被编译到独立的类中,现在我们使用内联方式将MyListItemRenderer修改如下:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
private function listCreationComplete():void
{
var iconList:Array=[];
var i:int=0;
for(i=0; i < 21; i++)
{
iconList.push({label:"卡通图标" + i, url:"headIcon/cartoon/cartoon" + i + ".png"});
}
myArr.source=iconList;
}
]]>
</mx:Script>
<mx:ArrayCollection id="myArr"/>
<mx:List id="lt" dataProvider="{myArr}" creationComplete="listCreationComplete()" rowHeight="45">
<mx:itemRenderer>
<mx:Component>
<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Image id="headIcon" source="{data.url}"/>
<mx:Label id="iconLabel" text="{data.label}"/>
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
</mx:Application>
由于内联中的组件被编译成独立的类文件,因此内联不同于包外类,内联带来了两个编程体验:
内联将单元渲染器和列表代码写在同一个mxml中,代码更加集中,当我们扩展列表组件并自定义渲染器时,可以体会到包外类方式的mxml创建方式。
内联所处的运行环境是独立的,但可以使用outerDocument属性访问包含它的mxml文件实例,比较便捷,outerDocument在创建内联时由Flex自动生成。
内联虽然访问父级的成员,但对封装性和垃圾回收并没有太大的影响,因为采取内联方式扩展的列表组件和自定义单元渲染器常常是一一对应关系。单元渲染器只是单方面引用所在的mxml中的成员,而外部并没有单独对单元渲染器进行引用,单元渲染器将和列表一并删除,不会造成内存溢出。
扩展默认渲染器
默认渲染器是重新创建自定义渲染器的经典例子,但扩展默认渲染器并不是很好的选择方式,默认渲染器大多已经定型且非容器,因此可扩展性不强,修改默认渲染器通常用于在此基础上增强视觉外观,例如:
<?xml version="1.0" encoding="utf-8"?>
<ListItemRenderer xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="mx.controls.listClasses.*" rollOver="changeFontSize()"
rollOut="restoreFontSize()" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
private var orginalFontSize:int;
private function changeFontSize():void
{
setStyle("fontSize",orginalFontSize+5);
}
private function restoreFontSize():void
{
setStyle("fontSize",orginalFontSize);
}
private function onCreationComplete():void
{
orginalFontSize=getStyle("fontSize") as int;
}
]]>
</mx:Script>
</ListItemRenderer>
空条目渲染器
当数据条目是一个稀疏数组时,可以指定空条目的渲染器,例如显示一张空缺的位图,空条目渲染器由ListBase的nullItemRenderer:Object属性定义,当条目为空且指定nullItemRenderer时,列表尝试创建空条目渲染器的实例。Flex不提供默认的空条目渲染器,如果不指定空条目渲染器则依然使用单元渲染器。
滚动列表
除了使用鼠标和键盘滚动列表外,可以调用scrollToIndex(index:int):Boolean方法滚动列表,index为需要显示在最上层的条目。如果条目已经可见则不会有任何变化。ListBase继承ScrollControlBase类,因此滚动能力是非常全面的,关于ScrollControlBase,请查看滚动自定义组件章节。
查找数据条目
虽然查找条目的通常做法是先通过数据源找到条目索引然后再设置selectedIndex或selectedItem属性,但ListBase还是提供了一个findString(str:String):Boolean的简单方法,findString通常查找条目的label属性和条目本身,不能设置细节的匹配规则,适用于简单的数据列表。
条目编辑器
不一定所有的列表都有条目编辑器,具有条目编辑器的列表在自身中定义条目编辑器,条目编辑器在设计上也同单元渲染器不同,详细情况将在子类中讲述。
列表事件
列表事件为ListEvent,ListEvent大多针对用户与单元渲染器的互动,ListEvent记录如下属性:
itemRenderer:IListItemRenderer 单元渲染器
rowIndex:int 条目行索引
columnIndex:int 条目列索引
reson:String 调度itemEditEnd事件的原因
其中最重要的是itemRenderer属性,它是通过鼠标操作获取单元渲染器引用的主要手段。rowIndex和columnIndex是条目索引,不是单元渲染器的信息记录中的位置,reson只对条目编辑器有意义。
ListBase为列表提供基本事件如下:
事件 描述
change selectedIndex和selectedItem改变时触发
itemClick 单击条目触发,即使未选中条目也会触发
itemDoubleClick 双击条目触发,需要将列表的doubleClickEnabled设置为true,无需设置单元渲染器的doubleClickEnabled属性,UIComponent在设置doubleClickEnabled时会使用递归设置所有子级的doubleClickEnabled属性。
itemRollOver 鼠标掠过单元渲染器
itemRollOut 鼠标掠出单元渲染器
点击和鼠标掠过事件在单元渲染器内部也可以侦听,这里的鼠标事件对象是列表组件自身,主要用于数据操作时的定位,例如拖放数据时进行判断,而视觉效果的代码仍然建议在单元渲染器的鼠标事件中完成。