也可以将内联 itemRenderer 用于 DataGrid。以下是应用于列的一个示例:
<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>
如您所见, 这次比前两个要复杂得多, 但结构相同: <mx:itemRenderer>
包含一个 <mx:Component>
定义。
<mx:Component>
是为了提供一个 MXML 语法, 用于在代码中创建一个 ActionScript 类。想象一下, 剪切 <mx:Component>
块中出现的代码并将它放入一个单独文件中并提供一个类名称。当您查看内联 itemRenderer 时, 它看上去就像一个完整的 MXML 文件, 不是吗?有根标记 (本例中为 <mx:HBox>
), 甚至 <mx:Script>
块。
本例中的 <mx:Script>
块用于覆盖 set data 函数, 使得 itemRenderer 的背景色可以更改。在本例中, 无论书籍的未来出版数据为何时, 背景将从白色更改为其他颜色。记住, itemRenderer 是循环使用的, 所以如果测试失败, 还必须将颜色设置回白色。否则, 当用户滚动列表时, 所有 itemRenderer 最终将变为紫色。
scope 也更改了。我的意思是, 从 <mx:Component>
中定义的变量仅作用于那个组件/内联 itemRenderer。同样, <mx:Component>
外的内容在不同的作用范围内, 就像这个组件是在另一个文件中定义的那样。例如, 假设您为这个 itemRenderer 添加了一个 Button, 允许用户从在线零售商那里购买书籍。Button 调用它们的 click
事件上的函数, 所以您可以如下定义这个按钮:
<mx:Button label="Buy" click="buyBook(data)" />
如果在文件的 <mx:Script>
块中定义 buyBook()
函数, 会显示一个错误, 指出 buyBook()
是一个未定义的方法。这是因为 buyBook()
是在文件的作用范围内而不是在 <mx:Component>
的作用范围内定义的。由于这是一个典型用例, 使用 outerDocument
标识符可以避开这个问题:
<mx:Button label="Buy" click="outerDocument.buyBook(data)" />
outerDocument
标识符将作用范围更改为查找文件或外部文档, 并引用 <mx:Component>
。现在请注意: 这个函数必须是公共函数, 而不是受保护或私有函数。记住, <mx:Component>
被视为外部定义的类。
现在我们来看另一个更复杂的示例。这是一个使用相同数据的 TileList。
<mx:TileList x="29" y="542" width="694" dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135" > <mx:itemRenderer> <mx:Component> <mx:HBox verticalAlign="top"> <mx:Image source="{data.image}" /> <mx:VBox height="115" verticalAlign="top" verticalGap="0"> <mx:Text text="{data.title}" fontWeight="bold" width="100%"/> <mx:Spacer height="20" /> <mx:Label text="{data.author}" /> <mx:Label text="Available {data.date}" /> <mx:Spacer height="100%" /> <mx:HBox width="100%" horizontalAlign="right"> <mx:Button label="Buy" fillColors="[0×99ff99,0×99ff99]"> <mx:click> <mx:Script> <![CDATA[ var e:BuyBookEvent = new BuyBookEvent(); e.bookData = data; dispatchEvent(e); ]]> </mx:Script> </mx:click> </mx:Button> </mx:HBox> </mx:VBox> </mx:HBox> </mx:Component> </mx:itemRenderer> </mx:TileList>
当应用程序运行时, itemRenderer 看上去如图 1:
这个 itemRenderer 与 DataGrid 中使用的那个十分相似, 但“购买”按钮的 click
事件不使用 outerDocument
调用函数。在本例中, click
事件会创建一个自定事件, 后者通过 TileList 从 itemRenderer 中冒出, 并由可视链中的较高组件接收。
这是一个很常见的问题: 您有一个 itemRenderer, 而它包含一些交互控制, 通常是 Button、LinkButton 或其他单击时会导致发生特定动作的组件。可能是删除行或是本例中的购买书籍。
指望 itemRenderer 完成这个工作并不合理。毕竟, itemRenderer 只负责让列表看上去美观。事件 bubbling 允许 itemRenderer 将这个工作转交给他人。自定事件此时派上了用场, 因为这个事件与行中的数据相关;为何不将数据包含在事件中呢?如果那样, 事件接收方就不必苦苦搜寻它了。
使用内联 itemRenderer 是一种快速自定列表外观的好方法。考虑将内联 itemRenderer 作为单独的 ActionScript 类-它们毕竟像有作用范围一样。如果必须引用包含文件中的函数或属性, 可使用 outerDocument
标识符更改作用范围。如果需要根据与 itemRenderer 的交互结果传达传递信息, 可使用自定冒泡事件。
并且记住: 不要试图抓住 itemRenderer 不放-它们将循环使用。它们只负责处理收到的数据。
我在下一篇文章中将讨论外部 itemRenderer。