SlideShare a Scribd company logo
1 of 45
Download to read offline
理解 Flex itemRenderer-Peter Ent



关于作者


Peter Ent 是 Adobe 的一名计算机科学家,目前正在进行 Genesis 项目。在此之前,Peter 担任过
Adobe 客户关怀的 Flex 支持工程师以及技术客户经理。加入 Adobe 之前,Peter 曾在 State Street and
Fidelity Investments 的金融服务行业工作。他还在两家新成立的公司构建过用于成像和分子建模的软件
应用程序。Peter 获得美国雪城大学的计算机科学理科学士学位。访问 Peter 的博客*。




第 1 部分: 内联渲染器


   Flex 提供许多控制, 它们可以按不同方式显示大量数据。List 控制自己、DataGrid、Tree 以

及包括图表和 AdvancedDataGrid 在内的可视类。
                              默认情况下, Flex 列表控制将提供的数据显示

为简单文本。但 Flex 的能耐远不止此, 列表控制提供了一种使用 itemRenderer 对其内容进行

自定的方式。通过允许您使用 itemRenderer 完全控制列表每行 (或单元格) 的内容, Flex 使您

能够编写出更引人注目、更具创意、更实用的应用程序。


   本系列讨论 Flex itemRenderer 以及如何高效、有效地使用它们。本系列的第一部分侧重于

内联 itemRenderer, 它们写入描述 List 控制的 MXML 标记中。之后的文章讨论使用 MXML

和 ActionScript 且更为复杂的 itemRenderer。


循环使用渲染器:


   人们经常尝试从列表外访问 itemRenderer。例如, 由于您刚从服务器那里收到新数据, 您可

能要将 DataGrid 中第五行的第四列变为绿色。获取那个 itemRenderer 实例并在外部修改它对

于 Flex 框架和组件模型而言是一个大工程。


   要理解 itemRenderer, 您必须理解它们为何变成现在这样以及我们当初设计它们时的目的。

在此, 当我用到“我们”时, 我指的是 Adobe Flex 设计小组。我与它没有关系。言归正传, 假设您
要显示 1,000 条记录。如果您认为列表控制会创建 1,000 个 itemRenderer, 您就错了。如果

列表只显示 10 行, 它会创建约 12 个 itemRenderer-这些足以显示各个可见行, 多出的几个

则用于缓冲和性能。列表最初显示行 1–10。当用户滚动这个列表时, 它现在可能显示行 3-12。

但那 12 个 itemRenderer 仍在那里: 及时滚动列表后, 也不会新建任何 itemRenderer。


    以下是 Flex 执行的操作。滚动列表时, 那些依然显示相同数据 (行 3-10) 的 itemRenderer

将向上移动。它们并未发生任何变化, 只是移到了新位置。之前显示行 1 和 行 2 数据的

itemRenderer 现在移到行 10 的 itemRendere 下。然后, 为那些 itemRenderer 提供行 11 和

行 12 的数据。换言之, 除非您调整列表大小, 否则将重用/循环使用那些相同的 itemRenderer

-它们只是移到新位置并且现在显示新数据。


    Flex 的这一行为在特定编程环境中使情况变得更复杂。例如, 如果要更改第五行的第四列中

某个单元格的背景色, 请注意如果用户滚动了该列表, 则该单元格的 itemRenderer 现在可能显

示第二十一行的内容。


那么怎样进行这类更改呢?


    itemRenderer 必须根据给它们显示的数据更改自己。如果列表的 itemRenderer 要根据数

据值更改其颜色, 它必须查看获得的数据并更改自己。



内联 IT EMRENDERER


    我在本文中使用内联 itemRenderer 说明如何解决这个问题。内联 itemRenderer 直接写入

MXML 文件中列表控制出现的位置。我在下一篇文章中将说明如何编写外部 itemRenderer。内

联 itemRenderer 最简单, 一般用于十分简单的渲染器或用于为较大的应用程序构建原型。内联

itemRenderer 本身并没有问题, 但随着代码变得复杂, 最好将它提取到自己的类中。
我将在所有示例中使用相同的数据: 一组书籍相关信息-作者、书名、出版日期和缩览图图

像等。每个记录是一段 XML 代码, 如下:



<book>


   <author>Peter F. Hamilton</author>


   <title>Pandora's Star</title>


   <image>assets/pandoras_star_.jpg</image>


<date>Dec 3, 2004</date>


</book>




    我将从使用 <mx:List> 控制的一个简单 itemRenderer 开始。这里将列出作者, 后跟书名。



<mx:List x="29" y="67" dataProvider="{testData.book}" width="286" height="190">


   <mx:itemRenderer>


         <mx:Component>


             <mx:Label text="{data.author}: {data.title}" />


         </mx:Component>


   </mx:itemRenderer>


</mx:List>




    这个 itemRenderer 太简单了, 可能使用 labelFunction 会更好, 但它至少允许您专注于重

要部分。首先, 内联 itemRenderer 使用 <mx:itemRenderer> 标记定义它。这个标记包含

<mx:Component> 标记。这个标记必须放在这里, 因为它会告诉 Flex 编译器您正在定义一个组

件内联。我马上会说明这到底是什么意思。
您在 <mx:Component> 标记中定义 itemRenderer。对于本例, 它是一个 <mx:Label> 并且

文本字段设置为一个数据绑定表达式: {data.author}: {data.title} 。这点很重要。List 控

制通过设置 itemRenderer 的 data 属性, 为每个 itemRenderer 实例提供 dataProvider 的记

录。对于上述代码, 它意味着对于任何给定列表行, 内联 itemRenderer 实例将自己的 data 属

性设置为 <book> XML 节点 (如以上节点)。当您滚动列表时, data 属性也会更改, 因为

itemRenderer 被循环用于新行。


    换言之, 行 1 的 itemRenderer 实例现在可能将其 data.author 设置为“Peter F.

Hamilton”, 但当它滚出视图时, itemRenderer 被循环使用并且 (该 itemRenderer) 的 data 属

性现在可能将其 data.author 设置为“J.K. Rowling”。滚动列表时, 所有这一切都会自动进行-

您不必操心。


    以下是复杂一些的内联 itemRenderer, 它还是使用 <mx:List> 控制:



<mx:List x="372" y="67" width="351" height="190" variableRowHeight="true"

dataProvider="{testData.book}">


  <mx:itemRenderer>


     <mx:Component>


         <mx:HBox >


             <mx:Image source="{data.image}" width="50" height="50" scaleContent="true" />


             <mx:Label text="{data.author}" width="125" />


             <mx:Text text="{data.title}" width="100%" />


         </mx:HBox>


     </mx:Component>


  </mx:itemRenderer>


</mx:List>
确实区别不大。这次不是 <mx:Label> , itemRenderer 是一个 <mx:HBox> 并包含

<mx:Image> 、<mx:Label> 和 <mx:Text> 控制。数据绑定依然将可视与记录关联在一起。



数据网格


    也可以将内联 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 最终将变为紫色。



OUT ERDOCUMENT


    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:




图 1.在 TileList 中实施一个 itemRenderer。


     这个 itemRenderer 与 DataGrid 中使用的那个十分相似, 但“购买”按钮的 click 事件不

使用 outerDocument 调用函数。在本例中, click 事件会创建一个自定事件, 后者通过 TileList

从 itemRenderer 中冒出, 并由可视链中的较高组件接收。


     这是一个很常见的问题: 您有一个 itemRenderer, 而它包含一些交互控制, 通常是 Button、

LinkButton 或其他单击时会导致发生特定动作的组件。可能是删除行或是本例中的购买书籍。


     指望 itemRenderer 完成这个工作并不合理。毕竟, itemRenderer 只负责让列表看上去美观。

事件 bubbling 允许 itemRenderer 将这个工作转交给他人。自定事件此时派上了用场, 因为这

个事件与行中的数据相关;为何不将数据包含在事件中呢?如果那样, 事件接收方就不必苦苦搜

寻它了。



结论


     使用内联 itemRenderer 是一种快速自定列表外观的好方法。考虑将内联 itemRenderer 作

为单独的 ActionScript 类-它们毕竟像有作用范围一样。如果必须引用包含文件中的函数或属

性, 可使用 outerDocument 标识符更改作用范围。如果需要根据与 itemRenderer 的交互结果

传达传递信息, 可使用自定冒泡事件。


     并且记住: 不要试图抓住 itemRenderer 不放-它们将循环使用。它们只负责处理收到的数

据。
我在下一篇文章中将讨论外部 itemRenderer。
第 2 部分: 外部渲染器

        在本系列的第 1 部分中, 我向您展示了如何创建内联 itemRenderer-这种 itemRenderer

    的 MXML 标记和 ActionScript 代码与使用该 itemRenderer 的列表位于同一文件中。代码与

    文件中的其余代码内联。


        您应该还记得我说过, 应该将内联 itemRenderer 视作单独的类。事实上, Flex 编译器提取

    这些内联代码并为您创建类。内联 itemRenderer 的优势在于代码与列表位于同一位置, 但是如

    果 itemRenderer 变得复杂时, 这又变成了劣势。本文中我将向您展示如何自己创建类。


        将 itemRenderer 提取到一个外部文件有几个优势:


    itemRenderer 可轻松用于多个列表中
    代码更容易维护
    可以使用 Flex Builder 的“设计”视图草拟出最初的 itemRenderer




    MXML IT EMRENDERER


        在第 1 部分中, 您看到有一个复杂的 itemRenderer 用于 DataGrid:



    <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>




    itemRenderer 基于 HBox, 包含一个 Image 和一个 Text, 并且根据项目记录

的 pubDate 字段设置背景色。可以使用以下步骤将同一 itemRenderer 编写为一个外部文件:


1. 如果您使用 Flex Builder, 请新建一个 MXML Component 文件 (我将我的文件命名为
   GridColumnSimpleRenderer, 您可以随意命名), 将根标记设置为 HBox 。不必担心大小。
2. 如果您只使用 SDK, 请新建一个 MXML 文件 (将它命名为
   GridColumnSimpleRenderer.mxml), 将根标记设置为 HBox 。

3. 在文件打开时, 复制 <mx:HBox> 与 </mx:HBox> 之间的所有内容, 但不要复制那些标记, 因

   为文件中已有它们。结果应该如下:



<?xml version="1.0" encoding="utf-8"?>


<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">


        <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>




4. 保存此文件。


现在修改 DataGridColumn 定义, 方法是删除内联 itemRenderer 并将它替换为以下内容:



<mx:DataGridColumn headerText="Title" dataField="title" itemRenderer="GridColumnSimpleRenderer">




现在运行这个应用程序。您会大吃一惊。因为行很高。这是因为 itemRenderer 上的

height="300"。



决定 IT EMRENDERER 的宽度和高度


    List 控制始终设置 itemRenderer 的宽度。本例中将忽略明确的 width="400"。您应当编

写自己的 itemRenderer, 假设用户更改列或列表宽度是宽度会更改。
高度则是另一回事。如果列表设置了明确的 rowHeight, 它会将这个高度强加到各行, 忽略

    您对 itemRenderer 设置的任何高度。但是, 如果您将列表的 variableRowHeight 属性设置

    为 true, 则列表会慎重考虑 itemRenderer 的高度。在本例中, 高度明确设置为 300, 所以各行

    为 300 像素高。


        要修复它, 请从 itemRenderer 文件中删除明确高度, 应用程序即可正确运行。




    动态更改 IT EMRENDERER


        本例覆盖了 set data() 函数以检查数据并设置 itemRenderer 的 backgroundColor 。这

    十分常见。覆盖 set data() 使您能截取为新行更改数据的时间, 并且您可以作出样式更改。


    常见错误为:


    忘记调用 super.data = value; 。这是致命错误-它会把 itemRenderer 弄乱。
    忘记重置样式 (如果任何测试失败)。当 pubDate 是将来时, 可能只设置颜色会比较诱人, 但您必须记
     住, itemRenderer 是循环使用的, 所以 else 语句很有必要。




    ACTIONSCRIPT ITEMRENDERER


        现在, 您将编写另一个 itemRenderer, 这次使用 ActionScript 类。在上一篇文章中, 有一

    个 TileList 包含这个内联 itemRenderer:



    <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="[0x99ff99,0x99ff99]">


          <mx:click>


          <![CDATA[


           var e:BuyBookEvent = new BuyBookEvent();


           e.bookData = data;


           dispatchEvent(e);


          ]]>


          </mx:click>


        </mx:Button>


      </mx:HBox>


     </mx:VBox>


   </mx:HBox>


 </mx:Component>


</mx:itemRenderer></mx:itemRenderer>




    您将把它转变为一个 ActionScript 外部 itemRenderer。您需要执行以下步骤:
1.新建一个 ActionScript 类。将它命名为 BookT ileRenderer.as 并使它扩展 HBox, 就像内联 itemRenderer
那样。



package


{


          import flash.events.MouseEvent;




          import mx.containers.HBox;


          import mx.containers.VBox;


          import mx.controls.Button;


          import mx.controls.Image;


          import mx.controls.Label;


          import mx.controls.Spacer;


          import mx.controls.Text;




          public class BookTileRenderer extends HBox


          {


                  public function BookTileRenderer()


                  {


                           super();


                  }


          }


}




2.创建成员变量, 用于存放子组件的引用。



private var coverImage:Image;
private var titleText:Text;


private var spacer1:Spacer;


private var authorLabel:Label;


private var pubdateLabel:Label;


private var spacer2:Spacer;


private var buyButton:Button;




3.   覆盖 createChildren() 函数以创建子组件, 并将它们添加到 HBox。



override protected function createChildren():void


{


        coverImage = new Image();


        addChild(coverImage);




        var innerBox:VBox = new VBox();


        innerBox.explicitHeight = 115;


        innerBox.percentWidth = 100;


        innerBox.setStyle("verticalAlign","top");


        innerBox.setStyle("verticalGap", 0);


        addChild(innerBox);




                 titleText = new Text();


                 titleText.setStyle("fontWeight","bold");


                 titleText.percentWidth = 100;


                 innerBox.addChild(titleText);
spacer1 = new Spacer();


spacer1.explicitHeight = 20;


innerBox.addChild(spacer1);




authorLabel = new Label();


innerBox.addChild(authorLabel);




pubdateLabel = new Label();


innerBox.addChild(pubdateLabel);




spacer2 = new Spacer();


spacer2.percentHeight = 100;


innerBox.addChild(spacer2);




var buttonBox:HBox = new HBox();


buttonBox.percentWidth = 100;


buttonBox.setStyle("horizontalAlign","right");


innerBox.addChild(buttonBox);




        buyButton = new Button();


        buyButton.label = "Buy";


        buyButton.setStyle("fillColors",[0x99ff99,0x99ff99]);


        buyButton.addEventListener(MouseEvent.CLICK, handleBuyClick);
buttonBox.addChild(buyButton);


}



我准备通过这一代码显示父子关系。同时, 确保在 Buy 按钮中包含一个事件侦听器。


4.   覆盖 commitProperties() 函数, 并从数据设置用户界面控制。



override protected function commitProperties():void


{


        super.commitProperties();




        coverImage.source = data.image;


        titleText.text = data.title;


        authorLabel.text = data.author;


        pubdateLabel.text = data.date;


}




5.为 Buy 按钮添加单击事件处理函数。



private function handleBuyClick( event:MouseEvent ) : void


{


        var e:BuyBookEvent = new BuyBookEvent();


        e.bookData = data;


        dispatchEvent(e);


}




6.   将 主 应 用 程 序 中 的            TileList 修 改 为 使 用      itemRenderer ActionScript 类 。 只 需 删 除
inlineItemRenderer 并将它替换为标记中的 itemRenderer 属性。


<mx:TileList id="mylist" x="29" y="542" width="694" itemRenderer="BookTileRenderer"


     dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135" >




如果要使用一个现有容器类, 如 HBox, 我不会使用 ActionScript 这样做。您会发现它比使用

MXML 文件复杂, 并且老实说, 性能方面几乎没有任何优势。



可重用的 IT EMRENDERER


以下是一个 itemRenderer 示例, 它使用 CurrencyFormatter 显示一个数值。我称之为

PriceFormatter:


<?xml version="1.0" encoding="utf-8"?>


<mx:Text xmlns:mx="http://www.adobe.com/2006/mxml">


 <mx:Script>


   <![CDATA[


     import mx.controls.dataGridClasses.DataGridListData;


     [Bindable] private var formattedValue:String;


     override public function set data(value:Object):void


     {


         super.data = value;


         formattedValue = cfmt.format( Number(data[(listData as DataGridListData).dataField]) );


     }


   ]]>


 </mx:Script>
<mx:CurrencyFormatter precision="2" id="cfmt" />


 <mx:text>{formattedValue}</mx:text>


</mx:Text>




    这个 itemRenderer 的关键部分以红色标出, 设置可绑定变量 formattedValue 。首先, 您

会发现 <mx:CurrentFormatter> 使用 id cfmt 定义为 MXML 标记 (如果您愿意, 也可以使

用 ActionScript 这样做)。在上例中, formattedValue 设置为 CurrentFormatter 的

format() 函数的调用结果。


    此函数将 Number 作为其参数类型, 所以值被转换为 Number-这是因为列表的

dataProvider 是 XML 并且 XML 中的所有内容是文本; 如果为数据使用 Object 并且您有实

际数值, 执行 Number 转变将是无害的。


    如您所知, 数据是存放 itemRenderer 所显示项目的属性。使用 [ ] 记号是访问数据项目字

段的另一种方法。例如, data['price'] 是价格列。但是为了使这个 itemRenderer 可重用, 您

不能为特定字段编码, 所以需要一种更普通的方法。


    此时, listData 登台亮相。实施 IDropInListItemRenderer 接口的所有 Flex 组件都有一

个 listData 属性。


注意: Text、Label、Button、CheckBox 等大多数控制都实施 IDropInListItemRenderer。HBox、

Canvas 等大多数容器不实施此接口。如果要在扩展 Container 的 itemRenderer 中使用

listData , 您必须自己实施 IDropInListItemRenderer; 我将在下一篇文章中讨论这个问题。


    除了其他内容, 提供给 itemRenderer 的 listData 还包含 rowIndex 和控制, 该控制拥有

itemRenderer-DataGrid、List 或 TileList。将 itemRenderer 用于 DataGrid 时, listData
实际上是一个 DataGridListData 对象-它包含 columnIndex 以及与 DataGridColumn 关联的

    dataField。以下是上述语句的明细, 从内部开始:


    listData as DataGridListData -它将 listData 转换为 DataGridListData 对象, 使您能访问
     它的 dataField
    .dataField -该字段用于渲染的列。它使这个 itemRenderer 变得一般。可以将这个 itemRenderer
     用于多个列。在本例中, dataField 是“price”。
    data[ ... ] -它访问项目中特定字段的数据。在本例中, 它是价格列。
    Number( ... ) -它将值转换为 Number, 因为 format() 函数需要一个 Number 参数。
    cfmt.format( ... ) -它将值格式化为货币。


    后续工作


    实施 itemRenderer 时, 可使用您喜欢的任何语言。一些人只适用 ActionScript, 当然如果您具

    有 Flex 和 ActionScript 方面的经验, 这很棒。MXML 也可以快速创建出简单的

    itemRenderer。


    在第 3 部分 (即将推出) 中, 我将进一步讨论 itemRenderer 与应用程序其余部分之间的通信。


    第 3 部分: 通信

        在本系列的第 2 部分中, 我向您展示了如何使用 MXML 和 ActionScript 创建外部

    itemRenderer。在我用过的示例中, 有一个调度自定事件 BuyBookEvent 的 Button-这样应用

    程序可以对它作出响应。本文进一步讨论与 itemRenderer 的通信。


        我坚信有一条规则是永远不能违背的: 不能抓住 itemRenderer 的实例不放, 更改它 (设置

    公共属性) 或调用它的公共方法。这对我而言是一个禁忌。itemRenderer 是很难弄清的, 我在第

    1 部分中说到了其中的缘由: itemRenderer 是循环使用的。抓住一个会破坏 Flex 框架。


    请将这条规则谨记在心, 您可以使用 itemRenderer 完成以下操作:


    itemRenderer 可以通过它的列表所有者调度事件。 (您已经看到过冒泡; 这个做法更好, 接下来您就会
     看到。)
    itemRenderer 可以使用静态类成员。其中包括 Application.application。如果您的值“全局”存储在应用
     程序对象中, 您可以通过这种方式获得它们。
    itemRenderer 可以使用拥有它的列表的公共成员。接下来您就会看到。
    itemRenderer 可以使用数据记录中的任何内容。例如, 记录中的某个项目不用于直接显示, 但它却影响
     itemRenderer 的行为方式。



    动态更改 IT EMRENDERER


        以下是上一篇文章中用于 TileList 的 MXML itemRenderer。我将使它对外部源 (我将这个

    文件称为 BookItemRenderer.mxml) 的更改作出反应, 从而使它更生动:



    <?xml version="1.0" encoding="utf-8"?>


    <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="250" height="115" >




            <mx:Script>


            <![CDATA[


            ]]>


            </mx:Script>


            <mx:Image id="bookImage" 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="[0x99ff99,0x99ff99]">


                                     <mx:click>
<![CDATA[


                                           var e:BuyBookEvent = new BuyBookEvent();


                                           e.bookData = data;


                                           dispatchEvent(e);


                                   ]]>


                                   </mx:click>


                           </mx:Button>


                 </mx:HBox>


         </mx:VBox>


</mx:HBox>




     假设您在 TileList 中显示一个商品目录。您还有一个 Slider (不属于 itemRenderer), 它允

许用户给出一个价格范围; 在此范围之外的所有商品应变灰 (itemRenderer 的 alpha 值应改

变)。您需要告知所有 itemRenderer 标准已改变, 这样它们可以修改自己的 alpha 值。


覆盖 set data 可能如下:




override public function set data( value:Object ) : void


{


    super.data = value;


    if( data.price < criteria ) alpha = 0.4;


         else alpha = 1;


}
问题是: 如何更改标准值?itemRenderer 的“最佳做法”是始终让它们处理为自己手头的数据。在

    本例中, 将测试标准作为数据的一部分不可能也不切实际。所以这在数据外留下了一个位置。您

    有两个选择:


       列表本身的一部分。即, 您的列表 (List、DataGrid、TileList 或其他) 可以是扩展列表控制并且将这个
        标准作为公共成员的类。
       作为全局数据的应用程序的一部分。


                我选择第一个: 扩展一个类并将这个标准作为该类的一部分。毕竟, 这个类将用于显示数据,

    而标准是显示内容的一部分。对于本例, 我将扩展 TileList 并将标准作为公共数据成员。



    package


    {


        import mx.controls.TileList;


        public class CatalogList extends TileList


        {


            public function CatalogList()


            {


                super();


            }


            private var _criteria:Number = 10;


            public function get critera() : Number


            {


                return _criteria;


            }


            public function set criteria( value:Number ) : void


            {
_criteria = value;


        }


    }


}




这个办法是, itemRenderer 外的控制可以通过更改列表控制上的这个属性来修改标准。



LIST DATA


itemRenderer 可以访问另一块数据: 列表本身的相关信息以及它们渲染哪个行哪个列 (如果在

面向列的控制中)。这称为 listData , 在 BookItemRenderer.mxml itemRenderer 示例中可以

这样使用它:



override public function set data( value:Object ) : void


{


        super.data = value;


        var criteria:Number = (listData.owner as MyTileList).criteria;


        if( data.price < criteria ) alpha = 0.4;


else alpha = 1;


}




            将这个代码放入上述示例 BooktItemRenderer.mxml 代码的 <mx:Script> 块中。


itemRenderer 的 listData property 有一个 owner 字段, 它是 itemRenderer 所属的控制。

在本例中, 所有者是 MyTileList 控制-我的 TileList 扩展。将所有者字段转换为

MyTileList 即可取走标准。
IDropInListItemRenderer
         当 itemRenderer 类实施 IDropInListItemRenderer 接口时, 即可访问 listData 。不幸的

是, UI 容器组件不实施提供 listData 访问权的接口。Button 和 Label 等控制组件提供这一访

问权, 但对于容器, 您必须自己实施接口。


         实施这个接口很简单, 您可以在 Flex 文档中找到说明。您必须为 BookItemRenderer 类执

行以下操作:


1. 由类实施接口。



     <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" ...


     implements="mx.controls.listClasses.IDropInListItemRenderer">




2.   将 set 和 get 函数添加到 itemRenderer 文件的 <mx:Script> 块。



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;


     }
当列表控制看到 itemRenderer 实施 IDropInListItemRenderer 接口时, 它将创建一个

listData 项目并将它指派到每个 itemRenderer 实例。


invalidateList()
     在我的类中设置这个标准并不像指派值这么简单。那样做无法告知 Flex 框架数据已更改。

标准更改必须触发一个事件。以下是对 set criteria() 函数的修改内容:



public function set criteria( value:Number ) : void


{


        _criteria = value;




        invalidateList();


}




     注意, 一旦设置 _criteria 值, 它会调用 invalidateList()。这将使用 dataProvider

的值重置所有 itemRenderer 并调用它们的 set data 函数。


随后的流程如下:


1. itemRenderer 在它的列表所有者中查找要使用的标准, 以帮助它决定如何渲染数据。
2. 列表所有者类是某个 Flex 列表类的扩展, 它包含 itemRenderer 读取以及外部代码-另一个控制或
     ActionScript 代码 (可能是从远程调用接收数据产生的) 设置的公共属性。
3. 设置列表属性时, 它调用列表的 invalidateList() 方法。这触发了 itemRenderer 刷新, 使它们重置
     自己的数据 (并返回第 1 步)。



活动


     在之前的文章中, 我向您展示了如何借助事件冒泡使 itemRenderer 与应用程序的其余部

分通信。我认为这相当快。但我认为还有更好的方法, 它符合 itemRenderer 负责展示数据、控

制负责处理数据的想法。
MyTileList 控制的构思是, 它是可售书籍目录的可见视图。当用户选中一本书籍并要购买

它时, 列表控制应当将这个信息发送给应用程序。换言之:


<CatalogList bookBuy="addToCart(event)" />


万事俱备, 事件冒泡并跳过 MyTileList。冒泡方法没有将事件 (bookBuy) 与列表控制

(MyTileList) 关联在一起, 允许您将控制移到应用程序的其他部分。例如, 如果您在主

Application 中写入 bookBuy 的事件侦听器, 您无法将列表控制移到应用程序的其他部分。如果

要移动, 您还得移动那个事件处理函数。另一方面, 如果您将事件与控制关联在一起, 只是移动控

制。


我们这样看: 假设 Button 上的单击事件并不是 Button 调度的事件, 但由于按钮中的某个内容

而冒泡。您将无法使用: <mx:Button click="doLogin()" label="Log in" />; 您必须将 doLogin()

函数放在其他位置, 这会使应用程序更难使用。


希望我已经说服您, 以下是如何将这个示例从冒泡更改为从列表控制调度。




1. 将元数据添加到 CatalogList 控制, 告知编译器控制负责调度事件:



  import events.BuyBookEvent;


       import mx.controls.TileList;


       [Event(name="buyBook",type="events.BuyBookEvent")]


       public class CatalogList extends TileList


       {




2. 将一个函数添加到 CatalogList 进行事件调度。itemRenderer 实例将调用这个函数:
public function dispatchBuyEvent( item:Object ) : void{


        var event:BuyBookEvent = new BuyBookEvent();


        event.bookData = item;


        dispatchEvent( event );


  }




3. 将 itemRenderer 中的 Buy 按钮代码更改为调用该函数:



  <mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]">


       <mx:click>


       <![CDATA[


             (listData.owner as CatalogList).dispatchBuyEvent(data);


       ]]>


       </mx:click>


  </mx:Button>




      现在, itemRenderer 中的 Button 可以使用记录数据 (或适合此操作的其他任何内容) 轻

松调用列表控制中的函数, 并将与应用程序其余部分协调的职责转交给列表控制。


      本例中的列表控制使用数据调度事件。应用程序可以使用 ActionScript 为这个事件添加事

件侦听器, 也可以使用 MXML (因为 CatalogList.as 文件中的 [Event] 元数据) ; 使

用 [Event]元数据使开发人员能更轻松地使用您的代码。



后续工作


      itemRenderer 应使用事件进行任何操作通信。自定事件允许您将信息随事件传递, 这样事

件使用者无需再通过 itemRenderer 获取数据。
itemRenderers 应通过覆盖它们的 set data 函数对数据更改作出“反应”。在该函数中, 它们

可以访问其 listData.owner 中的值。它们还可以访问存储在静态类或通过

Application.application 存储在主应用程序中的数据。


   在下一篇文章中, 我们将讨论如何将状态并入 itemRender 中。
第 4 部分: 状态和过渡

     itemRenderer 最擅长与应用程序的用户通信。有时通信很简单, 只是显示一个名称;有时

会使用更精致的颜色;有时具有交互性。


     itemEditor 是真正的交互控制, 但它们不是本文的重点。在这些示例中, 我将向您展示根据

数据本身或用户操作更改可视外观的 itemRenderer。



状态


     Flex <mx:State> 是更改 itemRenderer 外观的一个理想方法。状态使用简便, 并且与过渡

结合使用时, 可以为用户提供反馈和愉悦的体验。


     在本例中, 您将为列表新建一个 MXML itemRenderer(记住, 如果您愿意, 完全可以使用

ActionScript 这样做)。这个 itemRenderer 显示图像、书名、作者、价格以及用于购买这本书

籍的 Button。



<?xml version="1.0" encoding="utf-8"?>


<mx:HBox xmlns:mx="/2006/mxml">


     <mx:Image id="bookImage" source="{data.image}" />


     <mx:VBox height="115" width="100%" verticalAlign="top" verticalGap="0" paddingRight="10">


          <mx:Text text="{data.title}" fontWeight="bold" width="100%"/>


          <mx:Label text="{data.author}" />


          <mx:HBox id="priceBox" width="100%">


                  <mx:Label text="{data.price}" width="100%"/>


                  <mx:Button label="Buy" />


          </mx:HBox>


     </mx:VBox>
</mx:HBox>




        但是, 您想要实现的是, 如果书籍售空                                 ,
                           (数据包含 <instock> 节点, 它们是 yes 或 no) 价

    格和“购买”Button 会消失。我为价格和 Button 的 HBox 父代增加了一个 id 属性, 使此处的编

    码更简单一些。这样, 我可以通过更改 HBox 和 priceBox 的可视性来更改那些项目的可视性。


        通过覆盖 set data 函数可以这样做, 您将会这么做, 但不是直接更改 priceBox 的可视性,

    而是使用以下状态定义:



    <mx:states>


            <mx:State name="NoStockState">


                      <mx:SetProperty target="{priceBox}" name="visible" value="false"/>


            </mx:State>


    </mx:states>




    将以下内容放在根 <mx:HBox> 标记下。


        这个示例有些牵强, 因为它过于复杂, 不适合执行简单任务, 但它说明了如何使用状态。有两

    种状态:


    基本状态: 这是组件的正常状态。不使用状态的组件处于基本状态。在本例中, 基本状态的
     priceBox visible 属性为 true(默认)。这是当 instock 为 yes 时的情况。
    NoStockState : 这是当 instock 的值为 no 时的状态。处于这个状态时, 会执行 SetProperty 指
     令。target 属性决定了哪个类成员有问题, name 属性是目标上待更改属性的名称, value 是该属性的
     新值。


    set data 函数通过查看 instock 的值决定要使用哪个状态:



    override public function set data( value:Object ) : void
{


          super.data = value;




          if( data )


          {


                         if( data.instock == "yes" )


                                   currentState = "";


                         else


                                   currentState = "NoStockState";


          }


}




currentState 是所有 UIComponent 控制的属性。它决定了哪个状态是活动状态。在状态之

间切换时, Flex 框架从基本状态开始, 然后应用给定状态的规则。


注意: 请记住 itemRenderer 是循环使用的, 所以您必须始终恢复值;不要在 itemRenderer 中

保留 if 而没有 else。


如果您感觉风险较打, 可以删除本例中的 set data 覆盖。使用数据绑定表达式在根标记中直接

设置 currentState :



<mx:HBox xmlns:mx="/2006/mxml" width="400" currentState="{data.instock == 'yes' ? '' :

'NoStockState'}" >




通过检查与根标记内联的 data.instock 来设置 currentState 的值-不错的伎俩, 但是可能较难维护。
添加元素


在这个 itemRenderer 中, 仅当 instack 值为 yes 时才显示价格和“购买”按钮。当然不使用状

态也可以这样做, 但是, 如果 itemRenderer 要添加或删除更多控制, 状态更适合, 因为可以通

过设置 itemRenderer 的 currentState 属性对控制外观轻松实现控制。


您使用状态添加一个标签, 告诉用户该商品已售空, 而不是简单地删除价格和 Button。以下是修

改后的状态:



   <mx:states>


        <mx:State name="NoStockState">


             <mx:SetProperty target="{priceBox}" name="visible" value="false"/>


             <mx:AddChild relativeTo="{priceBox}" position="before">


                  <mx:Label text="-- currently not in stock --" color="#73DAF0"/>


             </mx:AddChild>


        </mx:State>


   </mx:states>




<mx:AddChild> 标记指明将 Label 添加到 priceBox 中。除了将 priceBox 的 visible 属性

设置为 false, 再使用一条友好的消息替换它。


再次, 您可以将这个标签添加到 set data 函数中-或按原样添加它, 只需将它的可视性设置

为 false 并在 set data 函数中将它更改为 true。但我认为您可以看到状态的值: 如果

instock 变为 no 条件的要求变得更复杂, 您只需调整 NoStockState;切换状态的

ActionScript 代码保持不变。
扩展列表项目


    本例不太适合 List 控制, 但十分适合 VBox 和 Repeater。当必须滚动列表时, 这个扩展适当位

    置项目的问题变得有些冒险。想象一下: 列表中的项目高度相同。现在扩展项目 2 的高度。目

    前进展顺利: 项目 2 比其他可见项目高。关键在这里: visible 项目。现在滚动列表。记住

    itemRenderer 是循环使用的。所以当项目 2 滚出视图时, 它的 itemRenderer 将移到列表底部。

    必须重置它的高度。没问题, 这个相当简单。现在滚动列表, 使项目 2 重新回到视图中。它应该

    是扩展后的高度。itemRenderer 怎么知道要这样做? 通过之前的文章, 您知道信息来自数据本

    身或某些外部源。


    我认为调整大小的 itemRenderer 太复杂, 并不值得这样做。我相信使用 VBox 和 Repeater 能

    以更好的方式做到这一点。但是, Repeater 的关键在于将创建每个子代。如果您有 1000 个记录

    并使用 Repeater, 您会获得 itemRenderer 的 1000 个实例。


    对于本例, 您依然编写一个 itemRenderer, 但将它用作 VBox 的子代。列表元素看上去相当简

    单: 一本书籍的名称及其作者。但是单击 itemRenderer, 它会扩展到位。我们使用两种战术做到

    这一点:


    itemRenderer 有一种包含附加信息的状态。
    itemRenderer 使用一种 Resize 过渡, 为 itemRenderer 提供更平滑的扩展和收缩。


    itemRenderer 的基本状态相当简单:



        <mx:HBox width="100%">


           <mx:Label text="{data.author}" fontWeight="bold"/>


           <mx:Text text="{data.title}" width="100%" fontSize="12" selectable="false"/>


        </mx:HBox>



    ExpandedState 添加其他元素, 它们用于 itemRenderer 的高度:
<mx:states>


        <mx:State name="ExpandedState">


             <mx:AddChild position="lastChild">


                  <mx:HBox width="100%">


                       <mx:Image source="{data.image}"/>


                       <mx:Spacer width="100%"/>


                       <mx:Label text="{data.price}"/>


                       <mx:Button label="Buy"/>


                  </mx:HBox>


             </mx:AddChild>


        </mx:State>


   </mx:states>



使用 itemRenderer 更改大小就像添加过渡一样简单:



    <mx:transitions>


        <mx:Transition fromState="*" toState="*">


             <mx:Resize target="{this}" />


        </mx:Transition>


   </mx:transitions>




将它放在 <mx:states> 标记下。


每当状态更改时应用过渡, 因为状态的 fromState 和 toState 属性是通配符。现在您只需添加

用于单击 itemRenderer(将一个 click 事件添加到根标记)的事件处理函数并更改状态:
<mx:Script>


   <![CDATA[




         private function expandItem() : void


         {


               if( currentState == "ExpandedState" )


                      currentState = "";


               else


                      currentState = "ExpandedState";


         }


   ]]>


   </mx:Script>




后续工作


状态是对 itemRenderer 的可视外观进行大量修改的好方法。您可以将多个更改归入一个 State,

然后设置 itemRenderer 的 currentState 属性就可以全部完成。


在下一篇文章中, 我们将讨论如何通过扩展 UIComponent 编写更高效的 itemRenderer。
第 5 部分: 效率

    如果您要显示大量 itemRenderer-无论在 DataGrid 还是在 AdvancedDataGrid 中-如果无

    法有效编写这些 itemRenderer, 您的应用程序性能可能受到负面影响。以下是一些可能有所帮

    助的提示:


    限制使用 itemRenderer 的列数。是否真的需要将每个列作为自定 itemRenderer?有时需要这样, 光
     芒太强是否会令用户头晕眼花?
    尝试不要过于频繁地更改 itemRenderer 中元素的样式。如果需要切换样式 (例如, 绿色用于正值, 红
     色用于负值) , 可考虑为这些样式创建两个控制预设并使其中一个可见。更改样式 Flex 中比较费时的任
     务之一。
    不要将 Containers 用作 itemRenderer 的基础。容器会占用大量资源。少量使用它们, 效果还不错; 但
     是根据 UIComponent 编写 itemRenderer 效率更高。



    切换样式


    以下 itemRenderer 根据数据字段的值切换组件。



    <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>




    这比设置样式要快。要注意的其他事项包括:


    避免数据绑定到样式。更改样式不仅比大多数操作要慢, 而且在它上面添加数据绑定代码只会使它更糟。
    使用 Canvas 或扩展 ListItemRenderer 或作为 itemRenderer 的根。这允许您将控制放在各自上方 。
扩展 UICOMPONENT


目前编写 itemRenderer 最有效的方式是使用 ActionScript 类扩展 UIComponent。您可以全

面控制代码, 并且渲染器也可以尽可能高效。


从上例切换样式开始, 编写一个扩展 UIComponent 的简单 itemRenderer。



package renderers


{


     import mx.controls.listClasses.IListItemRenderer;


     import mx.core.UIComponent;


     public class PriceItemRenderer extends UIComponent implements IListItemRenderer


     {


         public function PriceItemRenderer()


         {


              super();


         }


     }


}




您会发现我不仅编写这个类来扩展 UIComponent, 还由它实施 IListItemRenderer 接口。这样

做是有必要的, 因为 List 控制将需要任何渲染器实施该接口, 否则您会收到运行时错误, 因为

列表尝试将渲染器转换为该接口。


如果阅读 IListItemRenderer 的相关文档, 您会发现这是一个许多其他接口的集合,

UIComponent 会为您实施其中的大多数。但有一个 IListItemRenderer 扩展的接口是
UIComponent 不实施的:IDataRenderer。这需要您添加代码, 为 itemRenderer 类提供您一

    直使用的 data 属性。


    如果您尝试不实施 IDataRenderer 就使用这个类, 则编译代码时会收到以下错误:


       1044:类 renderers:PriceItemRenderer 未实施名称空间 mx.core:IDataRenderer 中的接口方法 get
        data。


    编辑这个类并将它更改为以下内容:



    package renderers


    {


           import mx.controls.listClasses.IListItemRenderer;


           import mx.core.UIComponent;


           import mx.events.FlexEvent;


           public class PriceItemRenderer extends UIComponent implements IListItemRenderer


           {


                public function PriceItemRenderer()


                {


                     super();


                }


         // Internal variable for the property value.


         private var _data:Object;


         // Make the data property bindable.


         [Bindable("dataChange")]


         // Define the getter method.
public function get data():Object {




        return _data;




    }


    // Define the setter method, and dispatch an event when the property




    // changes to support data binding.




    public function set data(value:Object):void {




        _data = value;




        dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));




    }


    }


}




我直接使用 IDataRenderer 的 Flex 文档中的代码, 这样您都不用自己键入它。


移除它后, 您可以加入两个标签。


1. 添加变量用于存放这两个标签。


    private var posLabel:Label; private var negLabel:Label;
2. 将 set data 函数修改为调用 invalidateProperties()。这很重要, 因为更改数据必须更

  改标签中的文本以及它们的可视性。



          public function set data(value:Object):void {


           _data = value;


           invalidateProperties();


           dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));


      }




  调用 invalidateProperties() 会告知 Flex 框架在适当的时间调用 commitProperties() 函数。

3. 覆盖 createChildren() 并创建标签, 添加它们以显示组件列表。注意, 除了创建标签外, 还会设置它们
  的样式和 visible 属性。



  override protected function createChildren() : void


  {


      super.createChildren();


      posLabel = new Label();


      posLabel.visible = false;


      posLabel.setStyle("color", 0×00FF00);


      addChild(posLabel);


      negLabel = new Label();


      negLabel.visible = false;


      negLabel.setStyle("color", 0xFF0000);


      addChild(negLabel);


  }
4. 覆盖 commitProperties() 以设置标签的文本和可视性。过去, 您一直通过覆盖 set data 进行这类更
  改, 如果您喜欢, 也可以在这个类中那样做。



  override protected


  function commitProperties():void


  {


      super.commitProperties();


      posLabel.text = data.price;


      negLabel.text = data.price;


      posLabel.visible = Number(data.price) > 0;


      negLabel.visible = Number(data.price) < 0;


  }




5. 覆盖 updateDisplayList() 以设置标签大小并进行定位。您必须设置标签大小, 因为它们的默认大小为
  0×0。这是 Container 类将为您做的另一件事。由于这个 itemRenderer 十分简单, 您只需将标签大小设置为
  与 itemRenderer 大小匹配即可。



  override protected function updateDisplayList(


  unscaledWidth:Number, unscaledHeight:Number ) : void


  {


      super.updateDisplayList(unscaledWidth, unscaledHeight);


      posLabel.move(0,0);


      posLabel.setActualSize(unscaledWidth,unscaledHeight);


      negLabel.move(0,0);


      negLabel.setActualSize(unscaledWidth, unscaledHeight);


  }




所有这些只能做到这点似乎有点复杂, 但是请记住一点, 使用容器增加的代码要比这多得多。
UICOMPONENT 附注


    UIComponent 类是所有可视 Flex 组件 (控制和容器) 的基础。以下是将 UIComponent 用作

    itemRenderer 的一些相关提示。


    UIComponent 对其子代 (如 Container) 没有版面限制。您必须自己进行子代定位并调整大小。
    还可以超出 updateDisplayList() 中指定的大小绘制图形和位置子代。
    如果准备在列表中使用 variableRowHeight, 您还应覆盖 measure() 函数, 告知列表
     itemRenderer 有多大。
    要将 UIComponent 用作 itemRenderer, 您必须实施 IDataRenderer。
    要使用 listData 属性, 您必须实施 IDropInListItemRenderer; 本系列之前的文章中讨论过这 个操作 。



    后续工作


    有关 Flex itemRenderer 的系列到此结束。希望您通过这些文章了解到使用 itemRenderer 可

    以在 Flex 应用程序中做些什么以及如何处理它们的数据、外观和行为的最佳做法。您可以在

    Adobe Flex 3 文档*中找到 Flex itemRenderer 的更多相关信息。

More Related Content

Similar to 理解 Flex item_renderer

Html5培训内容
Html5培训内容Html5培训内容
Html5培训内容Jun Yu
 
Flex 3 Cookbook 中文版V1
Flex 3 Cookbook 中文版V1Flex 3 Cookbook 中文版V1
Flex 3 Cookbook 中文版V1yiditushe
 
恶意网页分析实战
恶意网页分析实战恶意网页分析实战
恶意网页分析实战Huang Toby
 
HTML5 介绍
HTML5 介绍HTML5 介绍
HTML5 介绍S S
 
JavaScript Advanced Skill
JavaScript Advanced SkillJavaScript Advanced Skill
JavaScript Advanced Skillfirestoke
 
Open whisk quick start guide
Open whisk quick start guideOpen whisk quick start guide
Open whisk quick start guideJoseph Chang
 
面向开发的前端性能优化
面向开发的前端性能优化面向开发的前端性能优化
面向开发的前端性能优化li qiang
 
前端MVC之backbone
前端MVC之backbone前端MVC之backbone
前端MVC之backboneJerry Xie
 
Web设计 3 java_script初探(程序员与设计师的双重眼光)
Web设计 3 java_script初探(程序员与设计师的双重眼光)Web设计 3 java_script初探(程序员与设计师的双重眼光)
Web设计 3 java_script初探(程序员与设计师的双重眼光)ziggear
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现hua qiu
 
107个常用javascript语句 oss 计算技术 - ossez info of tech
107个常用javascript语句   oss 计算技术 - ossez info of tech107个常用javascript语句   oss 计算技术 - ossez info of tech
107个常用javascript语句 oss 计算技术 - ossez info of techYUCHENG HU
 
Learning JavaScript in Three Web Apps(中文)
Learning JavaScript in Three Web Apps(中文)Learning JavaScript in Three Web Apps(中文)
Learning JavaScript in Three Web Apps(中文)Dexter Yang
 
網站設計100步
網站設計100步網站設計100步
網站設計100步evercislide
 
第1讲 开始编写程序
第1讲 开始编写程序第1讲 开始编写程序
第1讲 开始编写程序ruandao
 
Javascript之昨是今非
Javascript之昨是今非Javascript之昨是今非
Javascript之昨是今非Tony Deng
 
Mongo db技术分享
Mongo db技术分享Mongo db技术分享
Mongo db技术分享晓锋 陈
 
Spring 2.x 中文
Spring 2.x 中文Spring 2.x 中文
Spring 2.x 中文Guo Albert
 

Similar to 理解 Flex item_renderer (20)

Ken20150320
Ken20150320Ken20150320
Ken20150320
 
Html5培训内容
Html5培训内容Html5培训内容
Html5培训内容
 
Flex 3 Cookbook 中文版V1
Flex 3 Cookbook 中文版V1Flex 3 Cookbook 中文版V1
Flex 3 Cookbook 中文版V1
 
恶意网页分析实战
恶意网页分析实战恶意网页分析实战
恶意网页分析实战
 
HTML5 介绍
HTML5 介绍HTML5 介绍
HTML5 介绍
 
JavaScript Advanced Skill
JavaScript Advanced SkillJavaScript Advanced Skill
JavaScript Advanced Skill
 
Open whisk quick start guide
Open whisk quick start guideOpen whisk quick start guide
Open whisk quick start guide
 
面向开发的前端性能优化
面向开发的前端性能优化面向开发的前端性能优化
面向开发的前端性能优化
 
前端MVC之backbone
前端MVC之backbone前端MVC之backbone
前端MVC之backbone
 
Web设计 3 java_script初探(程序员与设计师的双重眼光)
Web设计 3 java_script初探(程序员与设计师的双重眼光)Web设计 3 java_script初探(程序员与设计师的双重眼光)
Web设计 3 java_script初探(程序员与设计师的双重眼光)
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现
 
Flex 4 Skinning
Flex 4 SkinningFlex 4 Skinning
Flex 4 Skinning
 
107个常用javascript语句 oss 计算技术 - ossez info of tech
107个常用javascript语句   oss 计算技术 - ossez info of tech107个常用javascript语句   oss 计算技术 - ossez info of tech
107个常用javascript语句 oss 计算技术 - ossez info of tech
 
Learning JavaScript in Three Web Apps(中文)
Learning JavaScript in Three Web Apps(中文)Learning JavaScript in Three Web Apps(中文)
Learning JavaScript in Three Web Apps(中文)
 
網站設計100步
網站設計100步網站設計100步
網站設計100步
 
第1讲 开始编写程序
第1讲 开始编写程序第1讲 开始编写程序
第1讲 开始编写程序
 
Html&css基础
Html&css基础Html&css基础
Html&css基础
 
Javascript之昨是今非
Javascript之昨是今非Javascript之昨是今非
Javascript之昨是今非
 
Mongo db技术分享
Mongo db技术分享Mongo db技术分享
Mongo db技术分享
 
Spring 2.x 中文
Spring 2.x 中文Spring 2.x 中文
Spring 2.x 中文
 

理解 Flex item_renderer

  • 1. 理解 Flex itemRenderer-Peter Ent 关于作者 Peter Ent 是 Adobe 的一名计算机科学家,目前正在进行 Genesis 项目。在此之前,Peter 担任过 Adobe 客户关怀的 Flex 支持工程师以及技术客户经理。加入 Adobe 之前,Peter 曾在 State Street and Fidelity Investments 的金融服务行业工作。他还在两家新成立的公司构建过用于成像和分子建模的软件 应用程序。Peter 获得美国雪城大学的计算机科学理科学士学位。访问 Peter 的博客*。 第 1 部分: 内联渲染器 Flex 提供许多控制, 它们可以按不同方式显示大量数据。List 控制自己、DataGrid、Tree 以 及包括图表和 AdvancedDataGrid 在内的可视类。 默认情况下, Flex 列表控制将提供的数据显示 为简单文本。但 Flex 的能耐远不止此, 列表控制提供了一种使用 itemRenderer 对其内容进行 自定的方式。通过允许您使用 itemRenderer 完全控制列表每行 (或单元格) 的内容, Flex 使您 能够编写出更引人注目、更具创意、更实用的应用程序。 本系列讨论 Flex itemRenderer 以及如何高效、有效地使用它们。本系列的第一部分侧重于 内联 itemRenderer, 它们写入描述 List 控制的 MXML 标记中。之后的文章讨论使用 MXML 和 ActionScript 且更为复杂的 itemRenderer。 循环使用渲染器: 人们经常尝试从列表外访问 itemRenderer。例如, 由于您刚从服务器那里收到新数据, 您可 能要将 DataGrid 中第五行的第四列变为绿色。获取那个 itemRenderer 实例并在外部修改它对 于 Flex 框架和组件模型而言是一个大工程。 要理解 itemRenderer, 您必须理解它们为何变成现在这样以及我们当初设计它们时的目的。 在此, 当我用到“我们”时, 我指的是 Adobe Flex 设计小组。我与它没有关系。言归正传, 假设您
  • 2. 要显示 1,000 条记录。如果您认为列表控制会创建 1,000 个 itemRenderer, 您就错了。如果 列表只显示 10 行, 它会创建约 12 个 itemRenderer-这些足以显示各个可见行, 多出的几个 则用于缓冲和性能。列表最初显示行 1–10。当用户滚动这个列表时, 它现在可能显示行 3-12。 但那 12 个 itemRenderer 仍在那里: 及时滚动列表后, 也不会新建任何 itemRenderer。 以下是 Flex 执行的操作。滚动列表时, 那些依然显示相同数据 (行 3-10) 的 itemRenderer 将向上移动。它们并未发生任何变化, 只是移到了新位置。之前显示行 1 和 行 2 数据的 itemRenderer 现在移到行 10 的 itemRendere 下。然后, 为那些 itemRenderer 提供行 11 和 行 12 的数据。换言之, 除非您调整列表大小, 否则将重用/循环使用那些相同的 itemRenderer -它们只是移到新位置并且现在显示新数据。 Flex 的这一行为在特定编程环境中使情况变得更复杂。例如, 如果要更改第五行的第四列中 某个单元格的背景色, 请注意如果用户滚动了该列表, 则该单元格的 itemRenderer 现在可能显 示第二十一行的内容。 那么怎样进行这类更改呢? itemRenderer 必须根据给它们显示的数据更改自己。如果列表的 itemRenderer 要根据数 据值更改其颜色, 它必须查看获得的数据并更改自己。 内联 IT EMRENDERER 我在本文中使用内联 itemRenderer 说明如何解决这个问题。内联 itemRenderer 直接写入 MXML 文件中列表控制出现的位置。我在下一篇文章中将说明如何编写外部 itemRenderer。内 联 itemRenderer 最简单, 一般用于十分简单的渲染器或用于为较大的应用程序构建原型。内联 itemRenderer 本身并没有问题, 但随着代码变得复杂, 最好将它提取到自己的类中。
  • 3. 我将在所有示例中使用相同的数据: 一组书籍相关信息-作者、书名、出版日期和缩览图图 像等。每个记录是一段 XML 代码, 如下: <book> <author>Peter F. Hamilton</author> <title>Pandora's Star</title> <image>assets/pandoras_star_.jpg</image> <date>Dec 3, 2004</date> </book> 我将从使用 <mx:List> 控制的一个简单 itemRenderer 开始。这里将列出作者, 后跟书名。 <mx:List x="29" y="67" dataProvider="{testData.book}" width="286" height="190"> <mx:itemRenderer> <mx:Component> <mx:Label text="{data.author}: {data.title}" /> </mx:Component> </mx:itemRenderer> </mx:List> 这个 itemRenderer 太简单了, 可能使用 labelFunction 会更好, 但它至少允许您专注于重 要部分。首先, 内联 itemRenderer 使用 <mx:itemRenderer> 标记定义它。这个标记包含 <mx:Component> 标记。这个标记必须放在这里, 因为它会告诉 Flex 编译器您正在定义一个组 件内联。我马上会说明这到底是什么意思。
  • 4. 您在 <mx:Component> 标记中定义 itemRenderer。对于本例, 它是一个 <mx:Label> 并且 文本字段设置为一个数据绑定表达式: {data.author}: {data.title} 。这点很重要。List 控 制通过设置 itemRenderer 的 data 属性, 为每个 itemRenderer 实例提供 dataProvider 的记 录。对于上述代码, 它意味着对于任何给定列表行, 内联 itemRenderer 实例将自己的 data 属 性设置为 <book> XML 节点 (如以上节点)。当您滚动列表时, data 属性也会更改, 因为 itemRenderer 被循环用于新行。 换言之, 行 1 的 itemRenderer 实例现在可能将其 data.author 设置为“Peter F. Hamilton”, 但当它滚出视图时, itemRenderer 被循环使用并且 (该 itemRenderer) 的 data 属 性现在可能将其 data.author 设置为“J.K. Rowling”。滚动列表时, 所有这一切都会自动进行- 您不必操心。 以下是复杂一些的内联 itemRenderer, 它还是使用 <mx:List> 控制: <mx:List x="372" y="67" width="351" height="190" variableRowHeight="true" dataProvider="{testData.book}"> <mx:itemRenderer> <mx:Component> <mx:HBox > <mx:Image source="{data.image}" width="50" height="50" scaleContent="true" /> <mx:Label text="{data.author}" width="125" /> <mx:Text text="{data.title}" width="100%" /> </mx:HBox> </mx:Component> </mx:itemRenderer> </mx:List>
  • 5. 确实区别不大。这次不是 <mx:Label> , itemRenderer 是一个 <mx:HBox> 并包含 <mx:Image> 、<mx:Label> 和 <mx:Text> 控制。数据绑定依然将可视与记录关联在一起。 数据网格 也可以将内联 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); } ]]>
  • 6. </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 最终将变为紫色。 OUT ERDOCUMENT scope 也更改了。我的意思是, 从 <mx:Component> 中定义的变量仅作用于那个组件/内联 itemRenderer。同样, <mx:Component> 外的内容在不同的作用范围内, 就像这个组件是在另一
  • 7. 个文件中定义的那样。例如, 假设您为这个 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}" />
  • 8. <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>
  • 9. 当应用程序运行时, itemRenderer 看上去如图 1: 图 1.在 TileList 中实施一个 itemRenderer。 这个 itemRenderer 与 DataGrid 中使用的那个十分相似, 但“购买”按钮的 click 事件不 使用 outerDocument 调用函数。在本例中, click 事件会创建一个自定事件, 后者通过 TileList 从 itemRenderer 中冒出, 并由可视链中的较高组件接收。 这是一个很常见的问题: 您有一个 itemRenderer, 而它包含一些交互控制, 通常是 Button、 LinkButton 或其他单击时会导致发生特定动作的组件。可能是删除行或是本例中的购买书籍。 指望 itemRenderer 完成这个工作并不合理。毕竟, itemRenderer 只负责让列表看上去美观。 事件 bubbling 允许 itemRenderer 将这个工作转交给他人。自定事件此时派上了用场, 因为这 个事件与行中的数据相关;为何不将数据包含在事件中呢?如果那样, 事件接收方就不必苦苦搜 寻它了。 结论 使用内联 itemRenderer 是一种快速自定列表外观的好方法。考虑将内联 itemRenderer 作 为单独的 ActionScript 类-它们毕竟像有作用范围一样。如果必须引用包含文件中的函数或属 性, 可使用 outerDocument 标识符更改作用范围。如果需要根据与 itemRenderer 的交互结果 传达传递信息, 可使用自定冒泡事件。 并且记住: 不要试图抓住 itemRenderer 不放-它们将循环使用。它们只负责处理收到的数 据。
  • 11. 第 2 部分: 外部渲染器 在本系列的第 1 部分中, 我向您展示了如何创建内联 itemRenderer-这种 itemRenderer 的 MXML 标记和 ActionScript 代码与使用该 itemRenderer 的列表位于同一文件中。代码与 文件中的其余代码内联。 您应该还记得我说过, 应该将内联 itemRenderer 视作单独的类。事实上, Flex 编译器提取 这些内联代码并为您创建类。内联 itemRenderer 的优势在于代码与列表位于同一位置, 但是如 果 itemRenderer 变得复杂时, 这又变成了劣势。本文中我将向您展示如何自己创建类。 将 itemRenderer 提取到一个外部文件有几个优势:  itemRenderer 可轻松用于多个列表中  代码更容易维护  可以使用 Flex Builder 的“设计”视图草拟出最初的 itemRenderer MXML IT EMRENDERER 在第 1 部分中, 您看到有一个复杂的 itemRenderer 用于 DataGrid: <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;
  • 12. 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> itemRenderer 基于 HBox, 包含一个 Image 和一个 Text, 并且根据项目记录 的 pubDate 字段设置背景色。可以使用以下步骤将同一 itemRenderer 编写为一个外部文件: 1. 如果您使用 Flex Builder, 请新建一个 MXML Component 文件 (我将我的文件命名为 GridColumnSimpleRenderer, 您可以随意命名), 将根标记设置为 HBox 。不必担心大小。 2. 如果您只使用 SDK, 请新建一个 MXML 文件 (将它命名为 GridColumnSimpleRenderer.mxml), 将根标记设置为 HBox 。 3. 在文件打开时, 复制 <mx:HBox> 与 </mx:HBox> 之间的所有内容, 但不要复制那些标记, 因 为文件中已有它们。结果应该如下: <?xml version="1.0" encoding="utf-8"?> <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300"> <mx:Script>
  • 13. <![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> 4. 保存此文件。 现在修改 DataGridColumn 定义, 方法是删除内联 itemRenderer 并将它替换为以下内容: <mx:DataGridColumn headerText="Title" dataField="title" itemRenderer="GridColumnSimpleRenderer"> 现在运行这个应用程序。您会大吃一惊。因为行很高。这是因为 itemRenderer 上的 height="300"。 决定 IT EMRENDERER 的宽度和高度 List 控制始终设置 itemRenderer 的宽度。本例中将忽略明确的 width="400"。您应当编 写自己的 itemRenderer, 假设用户更改列或列表宽度是宽度会更改。
  • 14. 高度则是另一回事。如果列表设置了明确的 rowHeight, 它会将这个高度强加到各行, 忽略 您对 itemRenderer 设置的任何高度。但是, 如果您将列表的 variableRowHeight 属性设置 为 true, 则列表会慎重考虑 itemRenderer 的高度。在本例中, 高度明确设置为 300, 所以各行 为 300 像素高。 要修复它, 请从 itemRenderer 文件中删除明确高度, 应用程序即可正确运行。 动态更改 IT EMRENDERER 本例覆盖了 set data() 函数以检查数据并设置 itemRenderer 的 backgroundColor 。这 十分常见。覆盖 set data() 使您能截取为新行更改数据的时间, 并且您可以作出样式更改。 常见错误为:  忘记调用 super.data = value; 。这是致命错误-它会把 itemRenderer 弄乱。  忘记重置样式 (如果任何测试失败)。当 pubDate 是将来时, 可能只设置颜色会比较诱人, 但您必须记 住, itemRenderer 是循环使用的, 所以 else 语句很有必要。 ACTIONSCRIPT ITEMRENDERER 现在, 您将编写另一个 itemRenderer, 这次使用 ActionScript 类。在上一篇文章中, 有一 个 TileList 包含这个内联 itemRenderer: <mx:itemRenderer> <mx:Component> <mx:HBox verticalAlign="top"> <mx:Image source="{data.image}" />
  • 15. <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="[0x99ff99,0x99ff99]"> <mx:click> <![CDATA[ var e:BuyBookEvent = new BuyBookEvent(); e.bookData = data; dispatchEvent(e); ]]> </mx:click> </mx:Button> </mx:HBox> </mx:VBox> </mx:HBox> </mx:Component> </mx:itemRenderer></mx:itemRenderer> 您将把它转变为一个 ActionScript 外部 itemRenderer。您需要执行以下步骤:
  • 16. 1.新建一个 ActionScript 类。将它命名为 BookT ileRenderer.as 并使它扩展 HBox, 就像内联 itemRenderer 那样。 package { import flash.events.MouseEvent; import mx.containers.HBox; import mx.containers.VBox; import mx.controls.Button; import mx.controls.Image; import mx.controls.Label; import mx.controls.Spacer; import mx.controls.Text; public class BookTileRenderer extends HBox { public function BookTileRenderer() { super(); } } } 2.创建成员变量, 用于存放子组件的引用。 private var coverImage:Image;
  • 17. private var titleText:Text; private var spacer1:Spacer; private var authorLabel:Label; private var pubdateLabel:Label; private var spacer2:Spacer; private var buyButton:Button; 3. 覆盖 createChildren() 函数以创建子组件, 并将它们添加到 HBox。 override protected function createChildren():void { coverImage = new Image(); addChild(coverImage); var innerBox:VBox = new VBox(); innerBox.explicitHeight = 115; innerBox.percentWidth = 100; innerBox.setStyle("verticalAlign","top"); innerBox.setStyle("verticalGap", 0); addChild(innerBox); titleText = new Text(); titleText.setStyle("fontWeight","bold"); titleText.percentWidth = 100; innerBox.addChild(titleText);
  • 18. spacer1 = new Spacer(); spacer1.explicitHeight = 20; innerBox.addChild(spacer1); authorLabel = new Label(); innerBox.addChild(authorLabel); pubdateLabel = new Label(); innerBox.addChild(pubdateLabel); spacer2 = new Spacer(); spacer2.percentHeight = 100; innerBox.addChild(spacer2); var buttonBox:HBox = new HBox(); buttonBox.percentWidth = 100; buttonBox.setStyle("horizontalAlign","right"); innerBox.addChild(buttonBox); buyButton = new Button(); buyButton.label = "Buy"; buyButton.setStyle("fillColors",[0x99ff99,0x99ff99]); buyButton.addEventListener(MouseEvent.CLICK, handleBuyClick);
  • 19. buttonBox.addChild(buyButton); } 我准备通过这一代码显示父子关系。同时, 确保在 Buy 按钮中包含一个事件侦听器。 4. 覆盖 commitProperties() 函数, 并从数据设置用户界面控制。 override protected function commitProperties():void { super.commitProperties(); coverImage.source = data.image; titleText.text = data.title; authorLabel.text = data.author; pubdateLabel.text = data.date; } 5.为 Buy 按钮添加单击事件处理函数。 private function handleBuyClick( event:MouseEvent ) : void { var e:BuyBookEvent = new BuyBookEvent(); e.bookData = data; dispatchEvent(e); } 6. 将 主 应 用 程 序 中 的 TileList 修 改 为 使 用 itemRenderer ActionScript 类 。 只 需 删 除
  • 20. inlineItemRenderer 并将它替换为标记中的 itemRenderer 属性。 <mx:TileList id="mylist" x="29" y="542" width="694" itemRenderer="BookTileRenderer" dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135" > 如果要使用一个现有容器类, 如 HBox, 我不会使用 ActionScript 这样做。您会发现它比使用 MXML 文件复杂, 并且老实说, 性能方面几乎没有任何优势。 可重用的 IT EMRENDERER 以下是一个 itemRenderer 示例, 它使用 CurrencyFormatter 显示一个数值。我称之为 PriceFormatter: <?xml version="1.0" encoding="utf-8"?> <mx:Text xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import mx.controls.dataGridClasses.DataGridListData; [Bindable] private var formattedValue:String; override public function set data(value:Object):void { super.data = value; formattedValue = cfmt.format( Number(data[(listData as DataGridListData).dataField]) ); } ]]> </mx:Script>
  • 21. <mx:CurrencyFormatter precision="2" id="cfmt" /> <mx:text>{formattedValue}</mx:text> </mx:Text> 这个 itemRenderer 的关键部分以红色标出, 设置可绑定变量 formattedValue 。首先, 您 会发现 <mx:CurrentFormatter> 使用 id cfmt 定义为 MXML 标记 (如果您愿意, 也可以使 用 ActionScript 这样做)。在上例中, formattedValue 设置为 CurrentFormatter 的 format() 函数的调用结果。 此函数将 Number 作为其参数类型, 所以值被转换为 Number-这是因为列表的 dataProvider 是 XML 并且 XML 中的所有内容是文本; 如果为数据使用 Object 并且您有实 际数值, 执行 Number 转变将是无害的。 如您所知, 数据是存放 itemRenderer 所显示项目的属性。使用 [ ] 记号是访问数据项目字 段的另一种方法。例如, data['price'] 是价格列。但是为了使这个 itemRenderer 可重用, 您 不能为特定字段编码, 所以需要一种更普通的方法。 此时, listData 登台亮相。实施 IDropInListItemRenderer 接口的所有 Flex 组件都有一 个 listData 属性。 注意: Text、Label、Button、CheckBox 等大多数控制都实施 IDropInListItemRenderer。HBox、 Canvas 等大多数容器不实施此接口。如果要在扩展 Container 的 itemRenderer 中使用 listData , 您必须自己实施 IDropInListItemRenderer; 我将在下一篇文章中讨论这个问题。 除了其他内容, 提供给 itemRenderer 的 listData 还包含 rowIndex 和控制, 该控制拥有 itemRenderer-DataGrid、List 或 TileList。将 itemRenderer 用于 DataGrid 时, listData
  • 22. 实际上是一个 DataGridListData 对象-它包含 columnIndex 以及与 DataGridColumn 关联的 dataField。以下是上述语句的明细, 从内部开始:  listData as DataGridListData -它将 listData 转换为 DataGridListData 对象, 使您能访问 它的 dataField  .dataField -该字段用于渲染的列。它使这个 itemRenderer 变得一般。可以将这个 itemRenderer 用于多个列。在本例中, dataField 是“price”。  data[ ... ] -它访问项目中特定字段的数据。在本例中, 它是价格列。  Number( ... ) -它将值转换为 Number, 因为 format() 函数需要一个 Number 参数。  cfmt.format( ... ) -它将值格式化为货币。 后续工作 实施 itemRenderer 时, 可使用您喜欢的任何语言。一些人只适用 ActionScript, 当然如果您具 有 Flex 和 ActionScript 方面的经验, 这很棒。MXML 也可以快速创建出简单的 itemRenderer。 在第 3 部分 (即将推出) 中, 我将进一步讨论 itemRenderer 与应用程序其余部分之间的通信。 第 3 部分: 通信 在本系列的第 2 部分中, 我向您展示了如何使用 MXML 和 ActionScript 创建外部 itemRenderer。在我用过的示例中, 有一个调度自定事件 BuyBookEvent 的 Button-这样应用 程序可以对它作出响应。本文进一步讨论与 itemRenderer 的通信。 我坚信有一条规则是永远不能违背的: 不能抓住 itemRenderer 的实例不放, 更改它 (设置 公共属性) 或调用它的公共方法。这对我而言是一个禁忌。itemRenderer 是很难弄清的, 我在第 1 部分中说到了其中的缘由: itemRenderer 是循环使用的。抓住一个会破坏 Flex 框架。 请将这条规则谨记在心, 您可以使用 itemRenderer 完成以下操作:  itemRenderer 可以通过它的列表所有者调度事件。 (您已经看到过冒泡; 这个做法更好, 接下来您就会 看到。)
  • 23. itemRenderer 可以使用静态类成员。其中包括 Application.application。如果您的值“全局”存储在应用 程序对象中, 您可以通过这种方式获得它们。  itemRenderer 可以使用拥有它的列表的公共成员。接下来您就会看到。  itemRenderer 可以使用数据记录中的任何内容。例如, 记录中的某个项目不用于直接显示, 但它却影响 itemRenderer 的行为方式。 动态更改 IT EMRENDERER 以下是上一篇文章中用于 TileList 的 MXML itemRenderer。我将使它对外部源 (我将这个 文件称为 BookItemRenderer.mxml) 的更改作出反应, 从而使它更生动: <?xml version="1.0" encoding="utf-8"?> <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="250" height="115" > <mx:Script> <![CDATA[ ]]> </mx:Script> <mx:Image id="bookImage" 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="[0x99ff99,0x99ff99]"> <mx:click>
  • 24. <![CDATA[ var e:BuyBookEvent = new BuyBookEvent(); e.bookData = data; dispatchEvent(e); ]]> </mx:click> </mx:Button> </mx:HBox> </mx:VBox> </mx:HBox> 假设您在 TileList 中显示一个商品目录。您还有一个 Slider (不属于 itemRenderer), 它允 许用户给出一个价格范围; 在此范围之外的所有商品应变灰 (itemRenderer 的 alpha 值应改 变)。您需要告知所有 itemRenderer 标准已改变, 这样它们可以修改自己的 alpha 值。 覆盖 set data 可能如下: override public function set data( value:Object ) : void { super.data = value; if( data.price < criteria ) alpha = 0.4; else alpha = 1; }
  • 25. 问题是: 如何更改标准值?itemRenderer 的“最佳做法”是始终让它们处理为自己手头的数据。在 本例中, 将测试标准作为数据的一部分不可能也不切实际。所以这在数据外留下了一个位置。您 有两个选择:  列表本身的一部分。即, 您的列表 (List、DataGrid、TileList 或其他) 可以是扩展列表控制并且将这个 标准作为公共成员的类。  作为全局数据的应用程序的一部分。 我选择第一个: 扩展一个类并将这个标准作为该类的一部分。毕竟, 这个类将用于显示数据, 而标准是显示内容的一部分。对于本例, 我将扩展 TileList 并将标准作为公共数据成员。 package { import mx.controls.TileList; public class CatalogList extends TileList { public function CatalogList() { super(); } private var _criteria:Number = 10; public function get critera() : Number { return _criteria; } public function set criteria( value:Number ) : void {
  • 26. _criteria = value; } } } 这个办法是, itemRenderer 外的控制可以通过更改列表控制上的这个属性来修改标准。 LIST DATA itemRenderer 可以访问另一块数据: 列表本身的相关信息以及它们渲染哪个行哪个列 (如果在 面向列的控制中)。这称为 listData , 在 BookItemRenderer.mxml itemRenderer 示例中可以 这样使用它: override public function set data( value:Object ) : void { super.data = value; var criteria:Number = (listData.owner as MyTileList).criteria; if( data.price < criteria ) alpha = 0.4; else alpha = 1; } 将这个代码放入上述示例 BooktItemRenderer.mxml 代码的 <mx:Script> 块中。 itemRenderer 的 listData property 有一个 owner 字段, 它是 itemRenderer 所属的控制。 在本例中, 所有者是 MyTileList 控制-我的 TileList 扩展。将所有者字段转换为 MyTileList 即可取走标准。
  • 27. IDropInListItemRenderer 当 itemRenderer 类实施 IDropInListItemRenderer 接口时, 即可访问 listData 。不幸的 是, UI 容器组件不实施提供 listData 访问权的接口。Button 和 Label 等控制组件提供这一访 问权, 但对于容器, 您必须自己实施接口。 实施这个接口很简单, 您可以在 Flex 文档中找到说明。您必须为 BookItemRenderer 类执 行以下操作: 1. 由类实施接口。 <mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" ... implements="mx.controls.listClasses.IDropInListItemRenderer"> 2. 将 set 和 get 函数添加到 itemRenderer 文件的 <mx:Script> 块。 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; }
  • 28. 当列表控制看到 itemRenderer 实施 IDropInListItemRenderer 接口时, 它将创建一个 listData 项目并将它指派到每个 itemRenderer 实例。 invalidateList() 在我的类中设置这个标准并不像指派值这么简单。那样做无法告知 Flex 框架数据已更改。 标准更改必须触发一个事件。以下是对 set criteria() 函数的修改内容: public function set criteria( value:Number ) : void { _criteria = value; invalidateList(); } 注意, 一旦设置 _criteria 值, 它会调用 invalidateList()。这将使用 dataProvider 的值重置所有 itemRenderer 并调用它们的 set data 函数。 随后的流程如下: 1. itemRenderer 在它的列表所有者中查找要使用的标准, 以帮助它决定如何渲染数据。 2. 列表所有者类是某个 Flex 列表类的扩展, 它包含 itemRenderer 读取以及外部代码-另一个控制或 ActionScript 代码 (可能是从远程调用接收数据产生的) 设置的公共属性。 3. 设置列表属性时, 它调用列表的 invalidateList() 方法。这触发了 itemRenderer 刷新, 使它们重置 自己的数据 (并返回第 1 步)。 活动 在之前的文章中, 我向您展示了如何借助事件冒泡使 itemRenderer 与应用程序的其余部 分通信。我认为这相当快。但我认为还有更好的方法, 它符合 itemRenderer 负责展示数据、控 制负责处理数据的想法。
  • 29. MyTileList 控制的构思是, 它是可售书籍目录的可见视图。当用户选中一本书籍并要购买 它时, 列表控制应当将这个信息发送给应用程序。换言之: <CatalogList bookBuy="addToCart(event)" /> 万事俱备, 事件冒泡并跳过 MyTileList。冒泡方法没有将事件 (bookBuy) 与列表控制 (MyTileList) 关联在一起, 允许您将控制移到应用程序的其他部分。例如, 如果您在主 Application 中写入 bookBuy 的事件侦听器, 您无法将列表控制移到应用程序的其他部分。如果 要移动, 您还得移动那个事件处理函数。另一方面, 如果您将事件与控制关联在一起, 只是移动控 制。 我们这样看: 假设 Button 上的单击事件并不是 Button 调度的事件, 但由于按钮中的某个内容 而冒泡。您将无法使用: <mx:Button click="doLogin()" label="Log in" />; 您必须将 doLogin() 函数放在其他位置, 这会使应用程序更难使用。 希望我已经说服您, 以下是如何将这个示例从冒泡更改为从列表控制调度。 1. 将元数据添加到 CatalogList 控制, 告知编译器控制负责调度事件: import events.BuyBookEvent; import mx.controls.TileList; [Event(name="buyBook",type="events.BuyBookEvent")] public class CatalogList extends TileList { 2. 将一个函数添加到 CatalogList 进行事件调度。itemRenderer 实例将调用这个函数:
  • 30. public function dispatchBuyEvent( item:Object ) : void{ var event:BuyBookEvent = new BuyBookEvent(); event.bookData = item; dispatchEvent( event ); } 3. 将 itemRenderer 中的 Buy 按钮代码更改为调用该函数: <mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]"> <mx:click> <![CDATA[ (listData.owner as CatalogList).dispatchBuyEvent(data); ]]> </mx:click> </mx:Button> 现在, itemRenderer 中的 Button 可以使用记录数据 (或适合此操作的其他任何内容) 轻 松调用列表控制中的函数, 并将与应用程序其余部分协调的职责转交给列表控制。 本例中的列表控制使用数据调度事件。应用程序可以使用 ActionScript 为这个事件添加事 件侦听器, 也可以使用 MXML (因为 CatalogList.as 文件中的 [Event] 元数据) ; 使 用 [Event]元数据使开发人员能更轻松地使用您的代码。 后续工作 itemRenderer 应使用事件进行任何操作通信。自定事件允许您将信息随事件传递, 这样事 件使用者无需再通过 itemRenderer 获取数据。
  • 31. itemRenderers 应通过覆盖它们的 set data 函数对数据更改作出“反应”。在该函数中, 它们 可以访问其 listData.owner 中的值。它们还可以访问存储在静态类或通过 Application.application 存储在主应用程序中的数据。 在下一篇文章中, 我们将讨论如何将状态并入 itemRender 中。
  • 32. 第 4 部分: 状态和过渡 itemRenderer 最擅长与应用程序的用户通信。有时通信很简单, 只是显示一个名称;有时 会使用更精致的颜色;有时具有交互性。 itemEditor 是真正的交互控制, 但它们不是本文的重点。在这些示例中, 我将向您展示根据 数据本身或用户操作更改可视外观的 itemRenderer。 状态 Flex <mx:State> 是更改 itemRenderer 外观的一个理想方法。状态使用简便, 并且与过渡 结合使用时, 可以为用户提供反馈和愉悦的体验。 在本例中, 您将为列表新建一个 MXML itemRenderer(记住, 如果您愿意, 完全可以使用 ActionScript 这样做)。这个 itemRenderer 显示图像、书名、作者、价格以及用于购买这本书 籍的 Button。 <?xml version="1.0" encoding="utf-8"?> <mx:HBox xmlns:mx="/2006/mxml"> <mx:Image id="bookImage" source="{data.image}" /> <mx:VBox height="115" width="100%" verticalAlign="top" verticalGap="0" paddingRight="10"> <mx:Text text="{data.title}" fontWeight="bold" width="100%"/> <mx:Label text="{data.author}" /> <mx:HBox id="priceBox" width="100%"> <mx:Label text="{data.price}" width="100%"/> <mx:Button label="Buy" /> </mx:HBox> </mx:VBox>
  • 33. </mx:HBox> 但是, 您想要实现的是, 如果书籍售空 , (数据包含 <instock> 节点, 它们是 yes 或 no) 价 格和“购买”Button 会消失。我为价格和 Button 的 HBox 父代增加了一个 id 属性, 使此处的编 码更简单一些。这样, 我可以通过更改 HBox 和 priceBox 的可视性来更改那些项目的可视性。 通过覆盖 set data 函数可以这样做, 您将会这么做, 但不是直接更改 priceBox 的可视性, 而是使用以下状态定义: <mx:states> <mx:State name="NoStockState"> <mx:SetProperty target="{priceBox}" name="visible" value="false"/> </mx:State> </mx:states> 将以下内容放在根 <mx:HBox> 标记下。 这个示例有些牵强, 因为它过于复杂, 不适合执行简单任务, 但它说明了如何使用状态。有两 种状态:  基本状态: 这是组件的正常状态。不使用状态的组件处于基本状态。在本例中, 基本状态的 priceBox visible 属性为 true(默认)。这是当 instock 为 yes 时的情况。  NoStockState : 这是当 instock 的值为 no 时的状态。处于这个状态时, 会执行 SetProperty 指 令。target 属性决定了哪个类成员有问题, name 属性是目标上待更改属性的名称, value 是该属性的 新值。 set data 函数通过查看 instock 的值决定要使用哪个状态: override public function set data( value:Object ) : void
  • 34. { super.data = value; if( data ) { if( data.instock == "yes" ) currentState = ""; else currentState = "NoStockState"; } } currentState 是所有 UIComponent 控制的属性。它决定了哪个状态是活动状态。在状态之 间切换时, Flex 框架从基本状态开始, 然后应用给定状态的规则。 注意: 请记住 itemRenderer 是循环使用的, 所以您必须始终恢复值;不要在 itemRenderer 中 保留 if 而没有 else。 如果您感觉风险较打, 可以删除本例中的 set data 覆盖。使用数据绑定表达式在根标记中直接 设置 currentState : <mx:HBox xmlns:mx="/2006/mxml" width="400" currentState="{data.instock == 'yes' ? '' : 'NoStockState'}" > 通过检查与根标记内联的 data.instock 来设置 currentState 的值-不错的伎俩, 但是可能较难维护。
  • 35. 添加元素 在这个 itemRenderer 中, 仅当 instack 值为 yes 时才显示价格和“购买”按钮。当然不使用状 态也可以这样做, 但是, 如果 itemRenderer 要添加或删除更多控制, 状态更适合, 因为可以通 过设置 itemRenderer 的 currentState 属性对控制外观轻松实现控制。 您使用状态添加一个标签, 告诉用户该商品已售空, 而不是简单地删除价格和 Button。以下是修 改后的状态: <mx:states> <mx:State name="NoStockState"> <mx:SetProperty target="{priceBox}" name="visible" value="false"/> <mx:AddChild relativeTo="{priceBox}" position="before"> <mx:Label text="-- currently not in stock --" color="#73DAF0"/> </mx:AddChild> </mx:State> </mx:states> <mx:AddChild> 标记指明将 Label 添加到 priceBox 中。除了将 priceBox 的 visible 属性 设置为 false, 再使用一条友好的消息替换它。 再次, 您可以将这个标签添加到 set data 函数中-或按原样添加它, 只需将它的可视性设置 为 false 并在 set data 函数中将它更改为 true。但我认为您可以看到状态的值: 如果 instock 变为 no 条件的要求变得更复杂, 您只需调整 NoStockState;切换状态的 ActionScript 代码保持不变。
  • 36. 扩展列表项目 本例不太适合 List 控制, 但十分适合 VBox 和 Repeater。当必须滚动列表时, 这个扩展适当位 置项目的问题变得有些冒险。想象一下: 列表中的项目高度相同。现在扩展项目 2 的高度。目 前进展顺利: 项目 2 比其他可见项目高。关键在这里: visible 项目。现在滚动列表。记住 itemRenderer 是循环使用的。所以当项目 2 滚出视图时, 它的 itemRenderer 将移到列表底部。 必须重置它的高度。没问题, 这个相当简单。现在滚动列表, 使项目 2 重新回到视图中。它应该 是扩展后的高度。itemRenderer 怎么知道要这样做? 通过之前的文章, 您知道信息来自数据本 身或某些外部源。 我认为调整大小的 itemRenderer 太复杂, 并不值得这样做。我相信使用 VBox 和 Repeater 能 以更好的方式做到这一点。但是, Repeater 的关键在于将创建每个子代。如果您有 1000 个记录 并使用 Repeater, 您会获得 itemRenderer 的 1000 个实例。 对于本例, 您依然编写一个 itemRenderer, 但将它用作 VBox 的子代。列表元素看上去相当简 单: 一本书籍的名称及其作者。但是单击 itemRenderer, 它会扩展到位。我们使用两种战术做到 这一点:  itemRenderer 有一种包含附加信息的状态。  itemRenderer 使用一种 Resize 过渡, 为 itemRenderer 提供更平滑的扩展和收缩。 itemRenderer 的基本状态相当简单: <mx:HBox width="100%"> <mx:Label text="{data.author}" fontWeight="bold"/> <mx:Text text="{data.title}" width="100%" fontSize="12" selectable="false"/> </mx:HBox> ExpandedState 添加其他元素, 它们用于 itemRenderer 的高度:
  • 37. <mx:states> <mx:State name="ExpandedState"> <mx:AddChild position="lastChild"> <mx:HBox width="100%"> <mx:Image source="{data.image}"/> <mx:Spacer width="100%"/> <mx:Label text="{data.price}"/> <mx:Button label="Buy"/> </mx:HBox> </mx:AddChild> </mx:State> </mx:states> 使用 itemRenderer 更改大小就像添加过渡一样简单: <mx:transitions> <mx:Transition fromState="*" toState="*"> <mx:Resize target="{this}" /> </mx:Transition> </mx:transitions> 将它放在 <mx:states> 标记下。 每当状态更改时应用过渡, 因为状态的 fromState 和 toState 属性是通配符。现在您只需添加 用于单击 itemRenderer(将一个 click 事件添加到根标记)的事件处理函数并更改状态:
  • 38. <mx:Script> <![CDATA[ private function expandItem() : void { if( currentState == "ExpandedState" ) currentState = ""; else currentState = "ExpandedState"; } ]]> </mx:Script> 后续工作 状态是对 itemRenderer 的可视外观进行大量修改的好方法。您可以将多个更改归入一个 State, 然后设置 itemRenderer 的 currentState 属性就可以全部完成。 在下一篇文章中, 我们将讨论如何通过扩展 UIComponent 编写更高效的 itemRenderer。
  • 39. 第 5 部分: 效率 如果您要显示大量 itemRenderer-无论在 DataGrid 还是在 AdvancedDataGrid 中-如果无 法有效编写这些 itemRenderer, 您的应用程序性能可能受到负面影响。以下是一些可能有所帮 助的提示:  限制使用 itemRenderer 的列数。是否真的需要将每个列作为自定 itemRenderer?有时需要这样, 光 芒太强是否会令用户头晕眼花?  尝试不要过于频繁地更改 itemRenderer 中元素的样式。如果需要切换样式 (例如, 绿色用于正值, 红 色用于负值) , 可考虑为这些样式创建两个控制预设并使其中一个可见。更改样式 Flex 中比较费时的任 务之一。  不要将 Containers 用作 itemRenderer 的基础。容器会占用大量资源。少量使用它们, 效果还不错; 但 是根据 UIComponent 编写 itemRenderer 效率更高。 切换样式 以下 itemRenderer 根据数据字段的值切换组件。 <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> 这比设置样式要快。要注意的其他事项包括:  避免数据绑定到样式。更改样式不仅比大多数操作要慢, 而且在它上面添加数据绑定代码只会使它更糟。  使用 Canvas 或扩展 ListItemRenderer 或作为 itemRenderer 的根。这允许您将控制放在各自上方 。
  • 40. 扩展 UICOMPONENT 目前编写 itemRenderer 最有效的方式是使用 ActionScript 类扩展 UIComponent。您可以全 面控制代码, 并且渲染器也可以尽可能高效。 从上例切换样式开始, 编写一个扩展 UIComponent 的简单 itemRenderer。 package renderers { import mx.controls.listClasses.IListItemRenderer; import mx.core.UIComponent; public class PriceItemRenderer extends UIComponent implements IListItemRenderer { public function PriceItemRenderer() { super(); } } } 您会发现我不仅编写这个类来扩展 UIComponent, 还由它实施 IListItemRenderer 接口。这样 做是有必要的, 因为 List 控制将需要任何渲染器实施该接口, 否则您会收到运行时错误, 因为 列表尝试将渲染器转换为该接口。 如果阅读 IListItemRenderer 的相关文档, 您会发现这是一个许多其他接口的集合, UIComponent 会为您实施其中的大多数。但有一个 IListItemRenderer 扩展的接口是
  • 41. UIComponent 不实施的:IDataRenderer。这需要您添加代码, 为 itemRenderer 类提供您一 直使用的 data 属性。 如果您尝试不实施 IDataRenderer 就使用这个类, 则编译代码时会收到以下错误:  1044:类 renderers:PriceItemRenderer 未实施名称空间 mx.core:IDataRenderer 中的接口方法 get data。 编辑这个类并将它更改为以下内容: package renderers { import mx.controls.listClasses.IListItemRenderer; import mx.core.UIComponent; import mx.events.FlexEvent; public class PriceItemRenderer extends UIComponent implements IListItemRenderer { public function PriceItemRenderer() { super(); } // Internal variable for the property value. private var _data:Object; // Make the data property bindable. [Bindable("dataChange")] // Define the getter method.
  • 42. public function get data():Object { return _data; } // Define the setter method, and dispatch an event when the property // changes to support data binding. public function set data(value:Object):void { _data = value; dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); } } } 我直接使用 IDataRenderer 的 Flex 文档中的代码, 这样您都不用自己键入它。 移除它后, 您可以加入两个标签。 1. 添加变量用于存放这两个标签。 private var posLabel:Label; private var negLabel:Label;
  • 43. 2. 将 set data 函数修改为调用 invalidateProperties()。这很重要, 因为更改数据必须更 改标签中的文本以及它们的可视性。 public function set data(value:Object):void { _data = value; invalidateProperties(); dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); } 调用 invalidateProperties() 会告知 Flex 框架在适当的时间调用 commitProperties() 函数。 3. 覆盖 createChildren() 并创建标签, 添加它们以显示组件列表。注意, 除了创建标签外, 还会设置它们 的样式和 visible 属性。 override protected function createChildren() : void { super.createChildren(); posLabel = new Label(); posLabel.visible = false; posLabel.setStyle("color", 0×00FF00); addChild(posLabel); negLabel = new Label(); negLabel.visible = false; negLabel.setStyle("color", 0xFF0000); addChild(negLabel); }
  • 44. 4. 覆盖 commitProperties() 以设置标签的文本和可视性。过去, 您一直通过覆盖 set data 进行这类更 改, 如果您喜欢, 也可以在这个类中那样做。 override protected function commitProperties():void { super.commitProperties(); posLabel.text = data.price; negLabel.text = data.price; posLabel.visible = Number(data.price) > 0; negLabel.visible = Number(data.price) < 0; } 5. 覆盖 updateDisplayList() 以设置标签大小并进行定位。您必须设置标签大小, 因为它们的默认大小为 0×0。这是 Container 类将为您做的另一件事。由于这个 itemRenderer 十分简单, 您只需将标签大小设置为 与 itemRenderer 大小匹配即可。 override protected function updateDisplayList( unscaledWidth:Number, unscaledHeight:Number ) : void { super.updateDisplayList(unscaledWidth, unscaledHeight); posLabel.move(0,0); posLabel.setActualSize(unscaledWidth,unscaledHeight); negLabel.move(0,0); negLabel.setActualSize(unscaledWidth, unscaledHeight); } 所有这些只能做到这点似乎有点复杂, 但是请记住一点, 使用容器增加的代码要比这多得多。
  • 45. UICOMPONENT 附注 UIComponent 类是所有可视 Flex 组件 (控制和容器) 的基础。以下是将 UIComponent 用作 itemRenderer 的一些相关提示。  UIComponent 对其子代 (如 Container) 没有版面限制。您必须自己进行子代定位并调整大小。  还可以超出 updateDisplayList() 中指定的大小绘制图形和位置子代。  如果准备在列表中使用 variableRowHeight, 您还应覆盖 measure() 函数, 告知列表 itemRenderer 有多大。  要将 UIComponent 用作 itemRenderer, 您必须实施 IDataRenderer。  要使用 listData 属性, 您必须实施 IDropInListItemRenderer; 本系列之前的文章中讨论过这 个操作 。 后续工作 有关 Flex itemRenderer 的系列到此结束。希望您通过这些文章了解到使用 itemRenderer 可 以在 Flex 应用程序中做些什么以及如何处理它们的数据、外观和行为的最佳做法。您可以在 Adobe Flex 3 文档*中找到 Flex itemRenderer 的更多相关信息。