理解 Flex itemRenderer - 第 1 部分: 內聯渲染器

理解 Flex itemRenderer - 第 1 部分: 內聯渲染器

http://www.adobe.com/cn/devnet/flex/articles/itemrenderers_pt1.html

 

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 要根據數據值更改其顏色, 它必須查看獲得的數據並更改自己。

內聯 itemRenderer

我在本文中使用內聯 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 最終將變爲紫色。

outerDocument

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:

在 TileList 中實施一個 itemRenderer。

圖 1.在 TileList 中實施一個 itemRenderer。

這個 itemRenderer 與 DataGrid 中使用的那個十分相似, 但“購買”按鈕的 click 事件不使用 outerDocument 調用函數。在本例中, click 事件會創建一個自定事件, 後者通過 TileList 從 itemRenderer 中出, 並由可視鏈中的較高組件接收。

這是一個很常見的問題: 您有一個 itemRenderer, 而它包含一些交互控制, 通常是 Button、LinkButton 或其他單擊時會導致發生特定動作的組件。可能是刪除行或是本例中的購買書籍。

指望 itemRenderer 完成這個工作並不合理。畢竟, itemRenderer 只負責讓列表看上去美觀。事件 bubbling 允許 itemRenderer 將這個工作轉交給他人。自定事件此時派上了用場, 因爲這個事件與行中的數據相關;爲何不將數據包含在事件中呢?如果那樣, 事件接收方就不必苦苦搜尋它了。

結論

使用內聯 itemRenderer 是一種快速自定列表外觀的好方法。考慮將內聯 itemRenderer 作爲單獨的 ActionScript 類-它們畢竟像有作用範圍一樣。如果必須引用包含文件中的函數或屬性, 可使用 outerDocument 標識符更改作用範圍。如果需要根據與 itemRenderer 的交互結果傳達傳遞信息, 可使用自定冒泡事件。

並且記住: 不要試圖抓住 itemRenderer 不放-它們將循環使用。它們只負責處理收到的數據。

我在下一篇文章中將討論外部 itemRenderer。

 

發佈了90 篇原創文章 · 獲贊 0 · 訪問量 8789
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章