簡介: 最爲新一代 RIA 技術的典型框架,Adobe Flex 既有傳統桌面程序的交互相應性強,健壯性以及容易編程調試的特色,又有着 Web 程序容易部署,更豐富多彩的 UI,靈活的分佈式應用等優點。而 Flex Framework 提供的豐富的組件庫,以及健壯、規範的組件開發流程,更使得他成爲了展示層技術的首選。其中要想正確高效地開發 Flex 組件,對組件生命週期的必不可少。掌握組件的生命週期,能夠方便 UI 展現、優化組件執行性能,避免內存泄露。本文將從開發人員的角度,向您介紹 Flex 組件的生命週期中的重要階段,以及每一個階段在編碼過程當中應該注意的問題。學習的目的是爲了最終指導實踐,因此本文還會經過一個具體的例子,講解生命週期現實應用。也許以前您一直在開發 Flex 組件,可是學習本文以後,您會作的更好。html
學習本文,您須要有必定的 Flex 編程經驗,對 Actionscript 語言有必定的瞭解。java
開始以前程序員
在開始本篇文章以前,首先問你們兩個問題:第1、什麼是 Flex;第2、爲何選擇 Flex。做爲一個有着 Flex 開發經歷的編程人員,您可能會給出不少答案:Flex 是支持 RIA 技術的開發框架;Flex 是一個開發和部署 RIA 的平臺;Flex 是 Adobe 公司的開發工具 Flex Builder 等等,其實這些解釋都不錯,由於 Flex 原本就是對這一系列開發技術產品的歸納詞。那麼爲何選擇 Flex 呢 ? 您確定會給出不少 Flex 的優勢,好比說:開發方便,由於它提供了大量的組件庫可使用;跨平臺和瀏覽器,由於它運行在 Flash player 播放器中之中;健壯性:Flex2 及其之後的版本都基於 ActionScript 3.0,做爲 Flash Player9 的衍生產品,實現了從解釋到編譯語言的飛躍,具備更高的性能和安全性。不錯,這些印象都很對,由於 Adobe 公司對 Flex 設計的初衷就是,提供一個透明的平臺,使得每個基於 flash player 的開發人員(好比說 Flash 開發人員)均可以開發應用程序;又使得每個應用程序的開發人員(好比說熟悉 java 或者 .NET 的程序員)均可以像開發普通應用那樣地開發 RIA 應用。web
Flex 本質編程
提起 Flex 咱們不得不追述其發展歷史以及兩個很重要的名詞或者說技術,那就是 Flash 和 Flash Player。Flash 是 Adobe 推出的基於時間軸的交互式矢量圖和 Web 動畫的標準。通常被大量應用於互聯網網頁的矢量動畫設計。而 Flash Player,則是運行 Flash 程序的播放器,特別是 flash player 9 以後,他經過 Action Script 3.0 和新一代的虛擬機 AVM2 帶來了更增強大的運行時功能。下面咱們就來簡單類比和對比下這三者之間的關係,從而得出 Flex 的本質究竟是什麼。瀏覽器
Flex 和 Flash 程序有不少相同點:他們都基於 Action Script 語言;而且都會被編譯成一個 .swf 文件。Flash Player 是運行 Flash 和 Flex 應用的播放器或者說運行時環境。他經過讀取 swf 文件裏的二進制內容同 Flex 或者 Flash 應用進行交互,從而來指示他們執行某些操做,好比說加載圖片,繪製圖形,發送 HTTP 請求等等。Flash Player 提供了全部能夠執行的操做的 API,而 Flash 和 Flex 應用只可以作 Flash Player 容許他們作的事情。因此 Flash player 環境是 Flex 和 Flash 應用開發的基礎,Flex 的不少類都是 Flash player API 的子類。安全
Flex 和 Flash 應用都運行在相同的 Flash Player 裏,具備着一樣的用戶體驗。由於不管哪一種應用程序,他們都只包含指令,須要由 Flash Player 來執行這些指令。因此,Flash 和 Flex 程序的區別並非內容不一樣,而是他們建立內容的方式不一樣。框架
基於幀的 Flex分佈式
基於以上闡述,咱們知道了 Flex 程序的本質就是 Flash,他是面相程序員的 Flash 變種。Flex 程序的根 mx.managers.SystemManager 就是 Flash 類 flash.display.MovieClip 的擴展。因此,Flex 也能夠說成是基於時間軸的,只不過他在時間軸上的幀只有兩幀:第一幀是預加載,由 Flex 框架控制,也就是咱們在 Application 運行之初看到的進度條,被稱之爲 Preloader( 如圖 1 所示 );第二幀,就是咱們真正的應用程序。瞭解這點對於咱們理解 flex 組件的生命週期相當重要,由於幀的執行模式有一個重要的特色,那就是在一幀中會先執行腳本後渲染屏幕,即一般稱爲的 Elastic Racetrack 模型(如圖 2 所示)。 這個模型預示着過長的代碼執行會影響屏幕渲染的用戶體驗。Flex 組件構建在幀模型基礎上的,須要一樣遵行幀的這種執行模式。
書歸正傳,下面咱們就來介紹 Flex 的生命週期。
首先,Flex 組建的生命週期是什麼?它是指 Flex 框架如何同每個 Flex 組件進行交互,經過什麼方法來來控制 Flex 組件的產生、刷新和銷燬,以及各個組件又是如何和外界進行通信的。歸納地說,Flex 組件的生命週期能夠總結爲兩個模式、三個時期、四個方法、五個事件、七個階段。
同客觀世界的物質同樣,Flex 組件也有着從出生到銷燬的三個時期:初生階段、生長階段、銷燬階段。每一個階段又會經歷若干個重要步驟包括:構造、準備、裝配、驗證、提交、交互、拆卸和回收。其中,有些步驟組件在一輩子中只會經歷一次,有的則會伴隨生命週期重複若干。這些步驟會經過四個重要方法負責實現。那麼 Flex 組件如何通知 Flex Engineer 當前處於哪一個步驟,又如何告訴變成開發人員當前的狀態如何呢?他會經過派發五個不一樣的事件來進行內外交互;並經過驗證 - 提交模式(invitation-validation pattern)來響應更新,從而實現了一個延遲渲染的鬆耦合的事件模型。
爲了可以更加容易理解一下的講解,讓咱們來舉例說明,咱們來從 Flex 組件的基類 UIComponent 出發建立一個簡單的圖片查看器 ImageViewer(以下圖 3 所示), 而後以此爲例分別講解各個時期裏,Flex 框架對該組建都作了什麼。
組件的出生階段包括三個步驟:構造,配置,裝配和初始化。(分別對應了 4 個 protected 方法,constructor、createChildren,commitProperties,measure 和 updatedisplayList)
首先組件從構造函數(construct)開始出生。當您使用 new 操做符(如清單 1 所示)或者在 mxml 裏聲明一個組件的時候,構造函數就被調用了。經過調用構造函數,一個組件類的實例被建立在了內存裏。可是僅此而已。由於他是組件生命週期的最先部分,此時組件中的子元素尚未被建立,屬性值也不明,因此,一般咱們在這一階段裏所作的事情不多;可是他也是一個爲組件添加時間監聽器的好地方,由於一個組件的構造函數會且只會被調用一次。
private var myImageView : ImageViewer = new ImageViewer(); |
初生階段的第二個步驟是配置。在組件中定義的一些特性(properties)會在這個階段經過 set 方法被賦值。可是請注意,此時組件的子元素仍然沒有被建立,因此若是組件的某個屬性的 set 方法裏涉及到了對子元素或者其屬性的操做的話,請格外留意。如清單 2 所示,假設咱們把 ImageViewer
組件放到一個 Panel 容器裏 (),此時代碼的執行順序以下:
<mx:Panel width="100"> <sample:ImageViewer imageHeight="150"/> </mx:Panel> |
輸出順序:
Panel : constructor Panel.with : setter ImageViewer Calendar : constructor ImageViewer.imageHeight :setter |
因此 Adobe 建議開發人員在配置階段只緩存特性值,而把業務邏輯推遲到到之後的驗證階段(參見清單 3)。這就是以前提到的驗證 - 提交模式(invitation-validation pattern),關於這一律念咱們會在下面的章節作詳細說明。
public function set imageHeight(value:Number): void { if (_imageHeight != value) { _imageHeight = value; imageHeightChanged = true ; this .invalidateDisplayList() dispatchEvent( new Event( "imageHeightChanged" )); } } |
組件被構造以及賦值以後,並不會自動進入整個生命週期的循環,他必須通過裝配時期,及組件自身裝配到顯示列表(Display List)上。組件只有經過 addChild 或者 addChildAt 方法被轉配到 Display List 上,纔會依次進入到如下的生命週期時期,不然得話,之後的步驟和方法都不會被調用。爲此咱們能夠作這樣一個實驗。咱們在組件代碼裏添加 trace(清單 4),而後再分別執行應用程序 Test1.mxml(清單 5)和 Test1.mxml(清單 6),再來 對兩個輸出。
package dw.flex.sample { import mx.core.UIComponent; public class ImageViewer extends UIComponent { public function ImageViewer() { trace( "constructor" ); super(); } override protected function createChildren(): void { trace( "createChildren" ); super.createChildren() } override protected function commitProperties(): void { trace( "commitProperties" ); super.createChildren() } override protected function measure(): void { trace( "measure" ); super.createChildren() } override protected function updateDisplayList(w:Number, h:Number): void { trace( "createChildren" ); super.updateDisplayList(w,h) } } } |
清單 5. 應用程序 Test1 及其輸出
應用程序 Test1.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ import dw.flex.sample.ImageViewer; private var myImageView : ImageViewer = new ImageViewer(); ]]> </mx:Script> </mx:Application> |
輸出:
constructor |
清單 6. 應用程序 Test2 及其輸出
應用程序 Test2.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"> <mx:Script> <![CDATA[ import dw.flex.sample.ImageViewer; private var myImageView : ImageViewer = new ImageViewer(); override protected function createChildren(): void { super.createChildren(); addChild(myImageView); } ]]> </mx:Script> </mx:Application> |
輸出:
constructor createChildren commitProperties measure createChildren |
初始化階段發生在裝配以後,這個階段包含了 3 個子階段,派發 3 個事件。
組件派發 Preinitialize 事件。這個階段意味着組件已經被添加到了顯示列表(DisplayList)上。
調用 protected 方法 createChildren() 來生成子元素。在這個階段裏,您能夠覆蓋這個方法添加須要的子元素。
派發 initialize 事件。這意味着組件及其全部的子元素都已經被建立、配置裝備到了 DisplayList 上。
進入第一次驗證和提交階段。Flex 框架會經過 layoutManager 引擎來依次調用組件的三個驗證方法 invalidateProperties,invalidateSize() 和 invalidateDisplayList(). 以及其其分別對應的三個提交方法 CommitProperties(),measure() 和 updateDisplayList()。關於這三組方法究竟都是作什麼的,咱們會在如下的驗證階段詳細介紹。
最後配發 creationComplete 事件。
至此,組件的初始化階段完成,同時也意味着 Flex 組件的出生時期結束了。總結以上個步驟,咱們用圖 4 來標註組件出生時期的流程圖。
組件通過出生時期以後就進入了生活時期,這個時期意味着組件能夠被看見,被操做,甚至被刪除。而且做爲一個成熟的個體,組件能夠和外界進行交互,對更新請求進行響應。Flex 組件使用 Invalidation – Validation 模式來響應更新請求。
驗證 - 提交模式(Invalidation -Validation Pattern)
Flex 的生命週期是創建的幀模型基礎之上的,因此一樣遵循着先執行腳本後渲染屏幕的規則。爲了達到流暢的視覺體驗,咱們一般指望可以儘可能減小代碼執行的消耗,而給畫面渲染留下充足的時間,爲了實現這點 Flex 使用了 Invalidation – Validation 模式,來實現資源的有效利用。
Invalidation – Validation 模式即驗證提交 - 模式提供了一個低耦合的處理過程,將數據的改變(舊數據的實效)和數據的處理(對新數據的從新使用)區分開來,這樣作的好處是:
以清單 7 爲例,經過 Invalidation – Validation 模式,第二行的代碼的結果永遠不會生效,即不會也歷來沒有顯示到屏幕上。
myImageView.height = 50; myImageView. height = 100; |
那 Flex 框架是如何實現這一模式的呢?讓咱們來看一下 Flex 是如何將這一模式應用到組件上的。
首先咱們來看一下 Flex 可視化組件基類 UIComponent 是如和處理 set width() 方法的。
public function set width(value:Number):void { if(explicitWidth != value) { explicitWidth = value; invalidateSize(); } // 其餘代碼省略 } |
從清單 8 咱們能夠看出,UIComponent 在首先會去判斷屬性賦值是否改變,從而避免重複賦值帶來的消耗,而後調用 protected 方法 invalidateSize()。
invalidateSize()是 UIComponent 提供的驗證方法之一,它用來校驗與組件尺寸大小相關的屬性,當此方法被調用後,Flex 組件引擎 LayoutManager 會首先檢查該屬性的更新請求是否已經調用過 invalidateSize()方法,若是調用過,說明此類型的更改請求已經記錄在案,無需贅述。若是沒有,則會將這次請求記錄到 LayoutManager 校驗隊列上進行等待。那麼對 width 屬性值的更新何時生效呢? LayoutManager 會在監聽到下一次 RENDER 事件派發的時候,將組件推送到提交階段。
在提交階段,LayoutManager 獲得更新請求隊列,調用 commitProperties()方法使得屬性 width 的最新值(假設記錄在了 _width 私有變量上)得以生效。Flex 就是經過這種屬性值延遲生效的方法來保證每次用於渲染畫面的請求都是最新的,從而避免了沒必要要的資源浪費。
所以,在咱們的例子 ImageViewer 裏,咱們也使用了相似機制。(如清單 9 所示),只不過這裏咱們調用的是 invalidateProterties()方法。與 invalidateSize()相似,組件的 scale 屬性值會在提交階段經過調用 commitProperties()方法生效。
public function set scale(value : Number): void { if (_scale != value) { _scale = value; scaleChanged = true ; invalidateProperties(); dispatchEvent( new Event( "scaleChanged" )); } } |
驗證階段是組件在生活要經歷的第一步,具體地說是響應更新請求的第一步。做爲 invalidation-validation 循環的一份子他會在組件的生命週期中屢次執行。Flex 組件基類 UIcomponent 提供了三種驗證方法:invalidateProperties()、invalidateSize()和 invalidateDisplayList() 來分別響應組件對值相關,佈局相關和顯示相關屬性的更新請求。
組件會經過兩種方式進入到驗證階段:
第一種方式是在初始化階段,經過父節點調用 childrenCreated() 方法,組件進入第一次 Invalidation – validation 循環。這一次,三種驗證方法都會被自動調用以確保組件初始化成功。這類調用在組件的生命週期內只會經歷一次。
第二種方式是響應更新請求。組件的任何一方面特性發生更新請求後,都會進入到驗證階段。而此時,用戶須要根據特性的不一樣類別來自行決定調用哪一種驗證方法。仍是以清單 9 爲例,經過調用 invalidateProperties()方法,確保了在接下來的提交階段,更新會對值計算生效。
在這個時期,咱們一般不去作太多的事情,由於此時只是驗證階段,更新並不生效。咱們所要作的只是記錄這次更新,而且等待進入提交階段。
經過以上的介紹,咱們已經很清楚組件更新會在提交階段實際生效。和驗證階段的隨時更新隨時校驗不一樣,Flex 框架爲提交階段設計了線性執行的順序,將三個提交方法分爲了三個階段。順序調用三個提交方法:commitProperties(),measure()和 updateDisplayList()。這樣作的目的和每一個方法的功能有關。commitProperties()方法主要負責屬性值的計算和生效,所以首先執行,由於這些本身算過的值可能用於佈局,也可能用於顯示。這裏也能夠用來移除不須要的子節點,可是您須要經過標誌位來判斷子節點是否存在。
measure()方法用於根據組件的內容大小計算組件尺寸。因爲尺寸的計算是自下而上的,所組件子元素的大小改變都會隱試的觸發此方法。
UpdatedisplayList() 方法用來更新顯示列表(Display List)的佈局以及渲染。組件的佈局是一個自上而下的過程,因此在這個方法裏您須要對不只僅是組件設置佈局屬性,並且也要對子元素進行相應的調整。這裏也是使用 Flash 繪圖 API 的好地方。
這裏,咱們所要作的就是覆蓋(override)這幾個方法,在正確的時間、正確的地方作正確的事。
交互嚴格地說組件生命週期中的某種行爲,他是促使組件更新,推進驗證 - 提交循環的動力。
Flex 使用事件驅動的交互模式,來實現一種徹底鬆耦合的體系結構。簡單地說,任何組件若是想要告訴外界當前發生了什麼事或者即將發什麼事的話,他能夠派發一個事件,那麼在該類事件可及範圍內的任何組件均可以經過註冊該事件的監聽器的方式來對此類事件進行相應。關於 Flex 在事件機制處理方面信息的因爲超出了本文的範圍,這裏再也不多講,感興趣的讀者能夠關注後續教程《探索 Flex 的事件機制》,或者閱讀資料部分的相應文檔。
通常來講,組件的交互行爲有如下幾種:
用戶同組件的交互,好比說:輸入數據,縮放大小等等。
經過派發和響應事件。
應用(Application)或者父節點(parent)與組件的交互。好比說 ItemRenderer 實例和宿主對象之間的 data 傳遞。(關於 ItemRenderer 機制和實踐講解,也會有後續教程加以探討)
任何事物都會有一個歸宿,Flex 組件也不例外。當某個組件再也不須要的時候,咱們須要把他銷燬。(這聽起來有點殘酷,可是咱們不得不這麼作)
銷燬組件的一種方法是經過調用組件父親節點(parent)的 removeChild()方法,該方法會將他從顯示列表(Display List)上拆卸掉,而且檢查是否存在對此組件的引用,若是沒有的話,組件會被標記爲「能夠回收」,這預示着組件進入到了回收階段,而且能夠被 AVM 垃圾回收。
剛纔我麼提到了經過 removeChild()方法將組建拆卸掉之後,組件能夠被垃圾回收。這意味着該組件的實例會被從內存中徹底刪除掉,而且釋放資源。可是請注意,垃圾回收並不必定在此刻立刻發生,AVM 有着本身的垃圾回收時間。所以這個打了標籤的組件須要等待回收時刻的到來。
拆卸階段並非組件銷燬的必經階段。當某個組件被拆卸掉以後,若是該組件包含了子組件,而他們都不存在外界引用的話,全部的元素都會被標記爲「能夠回收」,也就是說該系統中的子組件並不須要進入到拆卸階段,也能夠在回收時刻到來的時候被 AVM 回收掉。那麼開發人員所須要注意的就是,在這個時刻發生以前,將引用去除掉。
到目前爲止咱們學習的可能是 Flex 生命週期的理論。學習理論的最終目的就是知道編程實踐,如今咱們來看一下生命週期是如何在例子 ImageViewer 裏是被貫徹執行的。
清單 10 顯示了組件 ImageViewer 的 createChildren() 方法。正如你們注意的那樣,在建立每個子節點的時候,首先判斷該節點是否存在是一個很好的習慣,這樣能夠防止子節點在某種狀況下已被實例化。是的,這種狀況是可能發生的。以清單 11 裏的 Table 類爲例, Table 裏的子節點 row 有一個 set()方法,而咱們從「初生階段」裏知道,裝配階段(set 方法調用)要早於初始化階段,那麼請你們思考一下清單 12 的執行結果會是如何。
override protected function createChildren(): void { super.createChildren(); if (! this.border){ createBorder(); } if (!controlBar){ controlBar = new UIComponent(); } if (!zoomInButton){ zoomInButton = new Button(); zoomInButton.label = "+" ; zoomInButton.addEventListener(MouseEvent.CLICK, zoomInButtonClickHandler); controlBar.addChild(zoomInButton); } if (!zoomOutButton){ zoomOutButton = new Button(); zoomOutButton.label = "-" ; zoomOutButton.addEventListener(MouseEvent.CLICK, zoomOutButtonClickHandler); controlBar.addChild(zoomOutButton); } // added controlBar the last time addChild(controlBar); } |
public class Table extends TableBase { private var _row : Row; public function getrow():Row { return this._row; } public function setrow(r : Row): void { _row = r; this.invalidateDisplayList(); } public function Table() { super(); } override protected function createChildren(): void { super.createChildren(); if (!_row){ _row = new Row(); } this.addChild(_row); } |
var t : Table = new Table(); t.row = new Row(); addChild(t); |
咱們也看到 controlBar 在該方法的末尾才被添加到 Display List 上,正如以前提到的那樣,咱們只在須要的時候裝配他。同時,此時也是爲子節點添加監聽器的好地方。
下面咱們來看一下 ImageViewer 組件在 commitProperties()裏都作了什麼(如清單 13 所示)。
CommitProperties()是驗證方法 invalidateProperties()所對應的提交方法,也是初始化階段會被調用的第一個提交方法。他的目的是使得任何經過 set 方法提交的數值更改生效。因此您能夠看到在清單 9 的 set scale()方法裏,按照 invalidation-validation 模式,咱們調用了 invalidateProperties()方法從而將值的生效延遲到了 commitProperties()裏,而且爲了作到 「局部更新」,咱們使用了標誌位 scaleChanged。
另外,這裏這裏一個奇怪的地方,那就是爲何在 commitProperties()會有添加子節點的操做呢?這也是咱們要特地舉例說明的一個地方。
對於有些子節點,他的誕生多是和某些屬性值相關聯的,也就是咱們在編程提示四里提到的動態建立或者數據驅動的子節點。這些子節點,因爲他們並非隨着組件的產生而產生,而是要受屬性值的變化而產生或者變化,甚至在某些狀況下,根本就不須要存在。因此咱們應該在值的提交階段,也就是 commitProperties()方法調用的時候,當新值真正生效的時候再去建立它。
override protected function commitProperties(): void { super.commitProperties(); if (sourceChanged){ if (! this.image){ image = new Image(); this.addChild(image); image.source = this._source; } else { image.source = this._source; } sourceChanged = false ; } if (scaleChanged){ this.imageWidth = this.imageWidth * this.scale; this.imageHeight = this.imageHeight *+ this.scale; scaleChanged = false ; } } |
measure()方法是組件自動計算尺寸大小的地方,在例子 ImageViewer 的 measure()方法裏(如清單 14 所示),咱們作的事不多。這是由於,咱們 ImageViewer 被設計爲:須要顯式指定大小(固然這裏只是爲了舉例方便,您能夠根據須要,製做能夠自動度量大小的組件)。即在應用時設置 width 和 height 值。如清單 15 所示:
在這種狀況家,measure()方法不會被調用。因此須要提請讀者注意的就是,一旦您的組件在組裝階段被設置了 with 和 height 屬性值,那麼請不要指望在 measure 裏會執行您的業務邏輯。
override protected function measure(): void { super.measure(); measuredWidth = Math.max(image.width, controlBar.getExplicitOrMeasuredWidth()) ; measuredHeight = image.height + controlBar.getExplicitOrMeasuredHeight(); measuredMinWidth = measuredWidth; measuredMinHeight = measuredHeight; } |
<sample:ImageViewer width="400" height="350" imageWidth="150" imageHeight="140" source="product.jpg" borderColor="0x000000"/> |
updateDisplayList()方法用於對組件內容進行佈局以及渲染,一切屏幕上可見的內容都須要在這裏被處理,因此 updateDisplayList()能夠說是最繁忙的一個提交方法,他所包含的實現能夠很是多。清單 16 中,咱們省略了部分代碼。只留下了須要講解的部分。
在 measure()方法裏咱們能夠獲取 Flex 自動計算的尺寸(若是被調用的話),這些數據須要在 updateDisplayList() 方法裏被應用處理,因此咱們會大量使用 setActualSize()方法,特別是子元素比較多的時候。
做爲提交方法,updateDisplayList()的最重要的職責之一就相對應的 invalidateDisplayList()方法的更新請求進行響應。好比說清單 16 裏方法就對清單 17 裏的 set imageWidth()方法進行了相應。而且就像在 commitProperties()部分裏介紹的那樣,這裏一樣使用了「標誌位」方法來進行「局部跟新」。
局部更新是廣泛應用於提交方法裏的一種技巧,由於咱們知道這三個提交方法是公用的,任何更新的提交都會在這幾個方法裏被處理。而每次更新均可能只是局部的更改,因此是當地使用標誌位方法進行局部更新能夠有效地優化代碼的執行。
最後要提到的是,渲染屏幕的職責也決定了 updateDisplayList()方法是調用 Flash Player 繪圖 API 的好地方。因此咱們在清單 16 中特地使用了 drawRect()方法爲圖片家了一個邊框。
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number): void { super.updateDisplayList(unscaledWidth, unscaledHeight); // 省略部分代碼 zoomInButton.setActualSize(50, 20); zoomOutButton.setActualSize(50, 20); var tmpx : Number = 20; controlBar.setActualSize(tmpx, 20); controlBar.move(0, unscaledHeight-25); zoomInButton.move(tmpx, 0); tmpx +=60; zoomOutButton.move(tmpx, 0); if (imageHeightChanged ||imageWidthChanged){ image.width = this.imageWidth;//w; image.height = this.imageHeight;//h - 20; var tempX : Number = x+ (w-imageWidth) * 0.5; var tempY : Number = y + (h-imageHeight) * 0.5; image.x = tempX; image.y = tempY; imageHeightChanged = false ; imageWidthChanged = false ; var g:Graphics = graphics; g.clear(); g.beginFill(0x6F7777); g.drawRect(tempX-1, tempY-1, imageWidth+2, imageHeight+2); g.endFill(); } } |
public function set imageWidth(value:Number): void { if (_imageWidth != value) { _imageWidth = value; imageWidthChanged = true ; this.invalidateDisplayList() dispatchEvent( new Event( "imageWidthChanged" )); } } |
本文對 Flex 生命週期的各個階段進行了分類和講解,根據各個時期的特色提出了一些編程方面的提示,而且經過實例分析將這些編程提示應用到實踐中,但願對您的 Flex 開發工做有所幫助。