文章來自:http://www.riachina.com/showtopic-8747.html
關於flex事件的講解
一.引
很多新人對Flex的事件機制都不太熟悉,在使用過程中難免會出現各種問題,這是一個非常普遍的問題,為了更快更好的協助大家,將介紹一下Flex中事件的各種機制和用法。
Flex的精髓之一就是事件和綁定機制,瞭解之後,能協助大家更靈活的設計程式,也對新手上路有一定的協助。
講解可能不太系統,也不全面,有很多沒有深入。如果高手看到後有疑問,歡迎指正。當然各位也可以提出自己的看法,或者經驗分享,謝謝。
二.事件機制介紹
1. 什麼是事件機制
事件可以看作是一種觸發機制,當滿足了一定的條件後,會觸發這個事件。比如MouseEvent就是指的當滑鼠進行操作之後觸發的一系列的事件。很多控制項中都有click事件,這個事件就是一個MouseEvent的執行個體,當點擊滑鼠後,系統會自動拋出一個名稱為click的MouseEvent事件(這種方法我們將在後面介紹到)。如果此時在click上註冊一個方法,那麼觸發該事件時就會執行這個方法。
大致
該對應的Flex主應用的mxml代碼
<mx:Script>
<![CDATA[
import mx.controls.Alert;
private function clickHandler(e:MouseEvent){
Alert.show(e.currentTarget.toString());
}
]]>
</mx:Script>
<mx:Button id="testBtn" click="clickHandler(event)" label="測試">
</mx:Button>
在我們寫代碼時,編輯器的代碼補全提示列表中,有很多不同的表徵圖,
那些帶有閃電的就是事件,三個小塊的就是樣式,空心圓圈的是屬性,實心圓點的是公有方法,還有一個是效果。
我們能在這個列表中看到的事件,我把它稱之為事件註冊通道。(官方仍然稱它為事件,但是它又和普通的事件含義不同。關於事件註冊通道會再下面講述到)
2. 事件註冊通道
上面說到了,這些通道是只能在mxml的代碼提示中可以看到的,他的作用就是給mxml組件提供 事件觸發時所執行的方法的註冊通道,而且能在代碼提示中可見,這樣給組件提供了很大的抽象的好處,我們可以很清楚的告訴組件的使用者,組件裡包含哪些事件給你調用。
為什麼把他區別對待?除了代碼提示外,他還有一些實現上的不同。
Button的click事件是繼承自核心類InteractiveObject,遺憾我們看不到他的源碼,但是說明了“事件註冊通道”是可以繼承的。
我們會在自訂事件中講述到如何聲明“事件註冊通道”。
3. 事件觸發方法
註冊通道中如果填入了函數,那麼就代表觸發該事件時,會執行這個方法。
click="clickHandler(event)"
我們看到這個方法有一個event對象作為參數傳入,新人可能會問到,這個event對象哪裡來的?我也沒聲明這個變數啊。他實際上是註冊通道傳給他的,預設變數名就是event。我們如果想在事件觸發時傳其他的參數,可以通過自定的事件對象來實現。
這個對象就是這個組件分發的事件對象,即type為“click”的MouseEvent的一個執行個體。
這個event對象包含了觸發該事件時的各種資訊,比如觸發事件對象是哪個,監聽對象是哪個,觸發時滑鼠點在哪裡等等,不同的event類會包含不同的屬性,比如KeyboardEvent包含了鍵盤點擊了哪個鍵。
我們也可以通過自訂一個事件類別,來傳遞我們自己想要的各種資訊。(這在後面將介紹到)
4. 事件分發(重點了)
最終繼承自EventDispatcher的對象都會含有dispatchEvent這個方法,他有一個參數,事件對象。
之前說到的事件註冊通道,他只是一個通道,實際上事件是由這個方法來分發出去的,通道只是一個管道而已。
他的作用就是分發一個事件對象,他的分發是沒有目的的,一種廣播形式的,Flex的事件監聽線程會接收到各種各樣的事件(我們稱之為捕獲事件,這在後面會介紹到),那麼哪種才是你要的事件,標識就通過事件的type屬性來區分。
1)事件對象
在分發事件時,將會分發一個事件對象出去。不管是那個事件類別,都是繼承自flash.events.Event對象的,他包含一些比較重要的屬性,type和bubbles。
type是事件的類型,事件監聽通過這個參數來識別是否是自己所監聽的事件。
bubbles是個布爾值,決定了該對象是否會向上傳遞。預設是false。什麼意思呢?畫個圖就明白了。
比如說,當button組件分發click事件對象時,設定的bubbles為false,那麼他的分發是這樣的
示意代碼
dispatchEvent(new MouseEvent( “click” , false ));
事件對象無法跨越組件本身,當然,除了之前講到的註冊通道(這樣就很形象了吧)
因此,如果沒有註冊通道,在Flex主應用中,就無法捕獲到這個button組件分發出的事件。
如果我們將Bubbles設為true,他看起來就是這樣
dispatchEvent(new MouseEvent( “click” , true ));
可以看到,這個事件可以跨過組件本身,到達Flex主應用裡。不止這樣,在協助手冊中明確說到,如果在傳遞過程中間一直沒有被捕獲的話,這個事件會逐層上傳,直到最終的stage,那時如果還沒被捕獲,這個事件就會被銷毀掉。
這樣一來,即使我們沒有click的事件通道,只要我們在Flex主應用中添加事件監聽器(addEventListener)那麼我們就可以獲得到這個分發出的click事件了。
那麼,註冊通道不是沒用了嗎?不是,之前說到過,註冊通道是現式的,可見的,因此如果你的組件要給其他人使用,那麼就非常一目瞭然,而不必知道你源碼中究竟分發了什麼事件。但是,不要監聽和註冊同一個事件,這樣會重複執行的。(後面將講到)
5. 事件監聽
在分發中,我們講到,如果不是通過註冊通道來調用觸發事件,那麼我們是需要一個監聽來捕捉的。如何捕捉到分發出的事件,就是通過事件的type值。
比如:
<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout="absolute" xmlns:comp
creationComplete='init()'
>
<mx:Script>
<![CDATA[
private function init(){
testBtn.addEventListener(“click”, clickHandler);
}
Flex的事件中都提供了一些靜態常量,讓我們調用,避免我們打錯了。因此這句話可以這麼寫
testBtn.addEventListener(MouseEvent.CLICK,clickHandler);
我們看到,監聽的回調方法中沒有傳遞參數,是的,這和通道的寫法有些不同,這裡的回調方法(即clickHandler)只是個引用,並不是代表方法的執行,他的含義是,告訴eventLinstener,如果捕捉到click事件,那麼就去找clickHandler,並執行它,event對象參數在執行時動態傳遞。(如果熟悉ajax的朋友這裡應該很容易懂了)
他作用起來就是這樣
如果你又註冊了click的事件通道,那麼這兩個都會生效,顯然這是多餘的。
6. 關於非同步和執行順序
以前的說法有誤,as裡是不存線上程概念的,在遠程請求時,結果事件、錯誤事件都是非同步。如果你需要處理結果,需要利用監聽,並在回調中擷取你的遠端資料。
而在處理本地事件時,他們仍然是同步的。(謝謝ltian 的指正)
非同步
可以看出,回調方法執行的順序甚至還不如dispatchEvent之後的方法。如果接下來的方法依賴於事件回調,那麼把接下來的方法寫到回調方法中去
三.綁定機制
在我們瞭解了事件機制後,那麼理解除綁定定就不難了。綁定其實也是事件機制的運用
1. 什麼是綁定
綁定的原理就是事件,在被綁定的對象上增加了改變事件的監聽,一旦某個被綁定對象改變後,就會分發一個“propertyChange”事件(預設的,也可以改變成自己定義的事件),在其他組件中,會有propertyChange的事件監聽,當捕捉到該事件後,則會去更新群組件的屬性並顯示。
綁定的作用在於,將Flex中的變數、類、方法等與組件的值進行綁定。例如,一個變數如果被綁定後,那麼引用該變數的組件的相關屬性也會發生改變。我們用一個執行個體來表示
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml layout="absolute" xmlns:comp
>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable]
private var isSelected:Boolean;
private function clickHandler(e:MouseEvent){
//Alert.show(e.currentTarget.toString());
isSelected=isSelected?false:true; //這句話的意思是如果isSelected為true,改變它為false,如果它為false,改變它為true;
Alert.show(isSelected.toString());
}
]]>
</mx:Script>
<mx:Button id="testBtn" click="clickHandler(event)" label="測試" />
<mx:CheckBox x="60" selected="{isSelected}" />
</mx:Application>
上述程式的效果就是,當點擊button時,button不是直接改變checkbox的選中狀態,而是改變isSelected這個變數,由於isSelected是被綁定了的,那麼會關聯的改變CheckBox的選中狀態。
這樣看起來有些多此一舉,完全可以直接改變checkbox的selected屬性,我只是為了示範一下效果。如果說你的checkbox是動態構造的上百個,你不會去一個個的改變他吧。
因此,我們多數會將一個資料來源進行綁定聲明,這樣引用了這個資料來源的控制項,比如datagrid,在資料來源發生了改變時,即使你不重新設定dataProvider,列表的資料也會重新整理。當然,還有很多應用等待你去嘗試。
如果這個代碼中取消了[Bindable]的聲明,會怎麼樣?isSelected不會改變了嗎?
isSelected會改變,我們alert出來的結果也會顯示結果改變了,但是checkbox的選擇狀態不會改變,因為當一個組件由建立到最終顯示出來時是經過很多方法的,比如addChild,commitProperties,updateDisplayList等,updataDisplayList則是類似重新整理顯示效果一樣的方法。
僅僅改變屬性,而不去更新顯示效果那麼組件不會因為屬性的改變而發生任何變化。
綁定的原理也是利用的事件分發。更複雜的綁定有待你去自己發現了
四. 自訂事件的分發
這部分就不長篇大論了,因為各位應該已經掌握了事件的原理,因此貼出示範源碼,並進行些簡單的解釋。
1. 自訂事件 components/MyEventTest.as
package components
{
import mx.events.FlexEvent;
public class MyEventTest extends FlexEvent
{
public static const ONCHANGE:String = "onChange";
public var eventInfo:String; //自訂的事件資訊
public function MyEventTest(s:String){
super(s); //如果在構造時不設bubbles,預設是false,也就是不能傳遞的。
eventInfo="這個事件是:"+s;
}
}
}
2. 自訂群組件 components/ComponentForEvent.as
package components
{
import flash.events.EventDispatcher;
//這個就是聲明事件註冊通道的方法了。name是事件對應的名稱,也就是之前提到的type。Type是該事件的類
[Event(name="onChange", type="components.MyEventTest")]
public class ComponentForEvent extends EventDispatcher
{
private var name:String;
public function changeName(newName:String){
this.name=newName;
dispatchEvent(new MyEventTest(MyEventTest.ONCHANGE) );
}
}
}
3. App.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:comp
>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
private function changeName(){
cfe.changeName("新名稱");
}
]]>
</mx:Script>
<mx:Button id="testBtn" click=" changeName ()" label="測試" />
<components:ComponentForEvent
id="cfe" />
</mx:Application>