第一次大作業分析【事件篇】

        前面一篇已經提到了整個項目的界面結構和實現,理論上應該沒什麼難度了。希望同學們都能夠做到。下面介紹主要的一些事件實現思路。

        在整個項目中基本的事件主要有那麼幾種:按鈕事件、HttpService相關事件、List相關事件和DataGrid相關事件。從易到難一個個的來分析。

        首先是最簡單的按鈕事件,按鈕總共有那麼幾個:“查看購物車”按鈕、“返回商店”按鈕、List中的“加入購物車”按鈕和DataGrid中的刪除按鈕。

        1、“查看購物車”按鈕和“返回商店”按鈕

        這兩個按鈕主要實現它們的Click事件,右擊按鈕可以生成自動事件處理函數【PS:大家要養成良好的編程習慣,就是在界面上放置好控件後首先要修改該控件的ID,這裏假定這兩個按鈕的ID分別是btnCartView和btnShop】。函數代碼就是用來改變當前的狀態爲相應的狀態名。現在就兩個狀態視圖,一個是默認的State1,一個是新建的cartView。點擊“查看購物車”切換到cartView,點擊“返回商店”切換回State1。本來這樣就可以了。但是我們考慮到List和DataGrid的數據加載問題,所以做了點改變,什麼改變呢?就是List是首先顯示的界面,需要綁定到商品列表,而且整個數據是一次綁定永不更改,所以在整個Application初始化時就綁定好了數據。但是DataGrid的數據是會變化的,所以在Application初始化時不做綁定,但是在點擊“查看購物車”時綁定數據顯示最新的購物車信息。那麼“返回商店”按鈕的點擊事件還是老樣子,就是改變當前的狀態,但是“查看購物車”按鈕的點擊事件就是除了改變當前的狀態之外,還要實現向服務器端請求最新購物車信息並加載到DataGrid上。具體實現後面會提到,目前先預留這個功能(預留的意思就是你在這個事件裏用註釋的方式寫上以後要實現的功能,例如://這裏實現遠程數據獲取)。

        2、HttpService相關事件

        在第1點中我們已經提到要從遠程服務器中獲取數據綁定到相應控件,主要兩個控件需要綁定,一個是List,一個是DataGrid。前面也提到List的綁定是在Application初始化時操作,DataGrid的綁定是在點擊“查看購物車”按鈕時操作。綁定之前當然首先需要從服務器端獲取到數據,那麼如何將FLEX與遠程服務器連接起來呢,方法有很多,我們這裏使用的是HttpService這個不可見的控件對象。

        那麼首先來介紹下HttpService【官方介紹】。 這裏簡單的說下,就是提供一個http相關的服務,方便Flex與遠程URL指定的文件進行連接,可以傳遞參數也可以獲取返回信息。主要調用的方法是send(可選擇帶參數send也可以無參數send),主要設置的屬性是method、url和resultFormat,主要實現的事件是result。

       正式開始:List的數據綁定前面說了是在Applicaton初始化時操作,那麼Application初始化會激發什麼事件呢?很多,這裏使用creationComplete事件。在該事件中我們發送帶參數的請求到遠程服務器。那麼我們發送給誰呢?創建一個HttpService對象,如下所示:

<s:HTTPService id="goodsListService" url="http://localhost/xmlAction.aspx" resultFormat="e4x" result="handleGoodsResult(event)"/>
        這個就表示我們創建了一個名爲goodsListService的HttpService,它將連接到localhost下的xmlAciton.aspx文件,然後當捕獲了遠程連接的成功返回後會激發result事件,從而調用處理函數handleGoodsResult函數,遠程連接的返回值格式是e4x類型。這裏要注意的是像HttpService這種不可見的控件對象是要放在<fx:Declarations>節點裏面的

        然後在前面提到的creationComplete事件處理函數handleCreationComplete中,我們就可以寫如下代碼:

var params:Object=new Object();//創建一個用來存儲發送參數的Object對象
params.mode="readGoods";//參數就一個,參數名爲mode,參數值爲readGoods
goodsListService.method="post";//請求遠程URL的方法是post
goodsListService.send(params);//發送帶參數的請求
        這樣我們就把一個帶參數的請求發送到了url指定的xmlAction.aspx頁。然後成功返回後調用result事件處理函數handleGoodsResult:
goodItems=new XMLListCollection(event.result.goodItem);
        其中這個goodItems是一個XMLListCollection類型的全局變量
[Bindable]
private var goodItems:XMLListCollection;
        爲什麼要在前面加上[Bindable],因爲這個變量我們需要用它來做List控件數據源dataProvide,所以應該是一個會自動更新的可綁定類型,就要加[Bindable]。

        然後我們就可以將List的dataProvide屬性設置爲該變量:

<s:List id="listGoods" includeIn="State1" x="0" y="0" width="100%" height="249" borderVisible="false" <strong><span style="color:#FF0000;">dataProvider="{goodItems}"</span></strong> itemRenderer="views.viewGoodsList">
        這樣在Application初始化完成後就能連接到遠程服務器,並根據傳遞的mode參數獲取返回值爲一個XMLListCollection對象作爲List的數據源。然後在List的項呈現器裏的每個控件上分派各個節點名,如{data.theID}就能將XML文件中的每個商品節點下的theID節點值顯示在該控件上。

        OK,綁定成功。

        同理,在點擊“查看購物車”按鈕事件處理函數中,也利用HttpService向遠程服務器文件發送請求,獲取返回值後作爲DataGrid的dataProvide。爲了避免兩者混淆我們另外建了一個HttpService,前面那個用來操作商品列表文件,新建的這個用來操作購物車列表文件。

<s:HTTPService id="cartInfoService" url="http://localhost/flexAPP/xmlAction.aspx"  resultFormat="e4x" result="handleCartInfoResult(event)"/>
var params:Object=new Object();
params.mode="readCartInfo";
cartInfoService.method="post";
cartInfoService.send(params);
cartInfoList = new XMLListCollection( event.result.cartItem );
[Bindable]
private var cartInfoList:XMLListCollection;
<s:DataGrid id="dg" includeIn="cartView" x="0" y="0" width="100%" height="272" <strong><span style="color:#FF0000;">dataProvider="{cartInfoList}"</span></strong>>
    <s:columns>
        <s:ArrayList>
            <s:GridColumn dataField="theID" headerText="商品編號"></s:GridColumn>
            <s:GridColumn dataField="theName" headerText="商品名稱"></s:GridColumn>
            <s:GridColumn dataField="totalPrice"headerText="商品總價"></s:GridColumn>
            <s:GridColumn dataField="totalNum" headerText="商品數量"></s:GridColumn>
            <s:GridColumn editable="false" headerText="操作" itemRenderer="views.viewDGActionCol"></s:GridColumn>
        </s:ArrayList>
    </s:columns>
</s:DataGrid>

        那麼到這裏,我們就利用了兩個HttpService實現了List和DataGrid的數據綁定,一個綁定在初始化時進行,一個綁定在點擊“查看購物車”按鈕時進行。

        3、List相關事件

        在List中我們還需要實現點擊“加入購物車”按鈕事件。本來這個按鈕是屬於List的,我們可以直接操作List的Click事件,但是這樣的話點擊List中某個商品的任意位置(不一定點到按鈕上)都會激發這個事件,而我們點擊這個按鈕對List的顯示沒有任何更改操作,所以我們可以直接在List的項呈現器文件中定義按鈕事件就行了。但是因爲該按鈕事件涉及到需要傳遞當前選中的商品信息到遠程服務器,所以在List的項呈現器文件中又要定義個HttpService控件對象,過程類似前面的綁定,但是需要傳遞多個參數過去:

<s:HTTPService id="goodsListService" url="http://localhost/flexAPP/xmlAction.aspx" resultFormat="e4x" result="handleGoodsResult(event)"/>
        “加入購物車”按鈕點擊事件:

var goodID:String=lbID.text;//獲取當前商品的ID
var goodName:String=lbName.text;//獲取當前商品的名稱
var goodIMG:String=lbIMG.text;//獲取當前商品的圖片文件名
var goodPrice:String=lbPrice.text;//獲取當前商品的價格
var params:Object=new Object();//創建一個用來存儲發送參數的Object對象
params.mode="writeGoods";//參數1,參數名爲mode,參數值爲readGoods
params.goodID=goodID;//參數2,參數名爲goodID,參數值爲goodID變量值
params.goodName=goodName;//參數3,參數名爲goodName,參數值爲goodName變量值
params.goodIMG=goodIMG;//參數4,參數名爲goodIMG,參數值爲goodIMG變量值
params.goodPrice=goodPrice;//參數5,參數名爲goodPrice,參數值爲goodPrice變量值
goodsListService.method="post";//post方式發送請求
goodsListService.send(params);//發送帶參數的請求
        這裏要注意的就是有多個參數需要發送到服務器,那麼這些參數值哪裏來,就是在這個List的項呈現器裏用控件綁定的數據:有些控件綁定數據後需要顯示出來,那不需要做其他額外操作,但是有些控件綁定數據後不需要顯示,那麼就要設置隱藏,在這裏隱藏某個控件僅僅設置visible爲false不夠,還要設置一個includeInLayout爲false。

        最後遠程服務器返回成功後調用result處理函數:

if(event.result.toString().indexOf("writeOK",0)==0)//如果遠程服務器的返回結果是writeOK(這個writeOK的值是有服務器端代碼指定的,可以自定義)
{
    Alert.show("添加成功!"); //那麼就彈出“添加成功”的對話框。
}
        如果點了“加入購物車”按鈕後會影響到該按鈕的父容器List,那麼就需要做額外的事情,但是現在這裏不需要。

        4、DataGrid相關事件

        在前面【界面篇】中有提到DataGrid上需要實現編輯和刪除的操作,編輯相對簡單點,首先根據【界面篇】中的介紹設置好商品數量的可編輯狀態,然後就可以去找合適的事件來實現了。在spark的DataGrid控件上有個很方便的事件:gridItemEditorSessionSave,專門用來做編輯後保存的。我們來實現該事件的處理函數dg_gridItemEditorSessionSaveHandler(函數名是自動生成的,由於DataGrid的ID命名個人習慣不同,所以函數名可能不同)。

var xmlNode:XMLList=new XMLList(dg.selectedItem);//定義一個XMLList類型的變量來接收當前選中項(編輯某項必須先選中該項的)
var goodID:String=xmlNode.theID;//然後獲取該項的商品ID
var goodNum:String=xmlNode.totalNum;//獲取該項的編輯後的商品數量
var params:Object=new Object();//定義參數對象
params.mode="editCartInfo";//設置參數1,參數名mode,參數值editCartInfo	
params.goodID=goodID;//設置參數2,參數名goodID,參數值goodID變量值
params.goodNum=goodNum;//設置參數3,參數名goodNum,參數值goodNum變量值
cartInfoService.method="post";//post發送
cartInfoService.send(params);//發送帶參數請求
        服務器端成功返回後執行result事件處理函數,這個函數在第2點中已經實現過一次了,所以我們要進行補充:

        原來的代碼:

cartInfoList = new XMLListCollection( event.result.cartItem );
        修改後的代碼:

if(event.result.toString()=="editOK")
{
    Alert.show("修改成功!");
    refreshIT(event);
}
else
    cartInfoList = new XMLListCollection( event.result.cartItem );
        也就是說如果服務器端的返回值是editOK(由服務器端代碼指定,可自定義),表示當前編輯成功,那麼就顯示“修改成功”對話框,並刷新DataGrid,否則的話就獲取購物車信息,綁定到DataGrid,那麼爲什麼不這樣寫呢:

if(event.result.toString()=="editOK")
{
    Alert.show("修改成功!");
}
cartInfoList = new XMLListCollection( event.result.cartItem );
        這樣寫的話就變成,修改成功的情況下,把editOK這個文本字符串作爲DataGrid的數據源,這樣就不對了。所以我們需要在修改成功的情況下自定義個refreshIT函數來刷新DataGrid的數據源:

var params:Object=new Object();
params.mode="readCartInfo";	
cartInfoService.method="post";
cartInfoService.send(params);
        refreshIT函數的代碼和點擊“查看購物車”按鈕Click事件處理函數代碼類似。到這裏我們的編輯事件也完成了。接下來就剩下最後一個刪除了。刪除功能想象貌似挺簡單的,不就和List中的“加入購物車”按鈕差不多嘛,你錯了,真的錯了,這就是個坑,一個比較深的坑。爬出來挺花時間的,幸好我不算矮,也還挺厚的,有我墊底,你們爬的會快點。

        言歸正傳,我們來看重中之重。

        List中的“加入購物車”按鈕點了之後對List是不產生任何影響的,裏面的數據是不用刷新的,但是DataGrid裏的“刪除”按鈕不同,點了之後要重新加載DataGrid的數據源。所以問題來了,“刪除”按鈕是放在DataGrid的項呈現器MXML文件中的,而前面定義的重新加載DataGrid的自定義函數refreshIT是放在主MXML文件中的,如何在外部文件中調用主文件中的函數就待攻克的難關了。

        首先我們來看項呈現器裏的“刪除”按鈕Click事件處理函數:

var goodID:String=lbID.text;//獲取刪除按鈕所在商品的goodID的值(利用一個隱藏的Lable存放goodID的值)
var params:Object=new Object();//聲明參數對象
params.mode="delCartInfo";//設置參數1,參數名mode,參數值delCartInfo
params.goodID=goodID;//設置參數2,參數名goodID,參數值goodID變量值
goodsListService.method="post";//post發送
goodsListService.send(params);//帶參發送請求
        當然要事先定義好HttpService控件對象,這些代碼應該說已經比較熟悉了,應該沒什麼大問題,然後成功返回後實現result事件處理函數:

if(event.result.toString().indexOf("delOK",0)==0)//如果返回值是delOK(服務器端代碼指定,可自定義)
{
    var e:Event = new Event("refreshDG",true);//定義一個新的事件對象,重點,下面詳述
    var flag:Boolean=this.dispatchEvent(e);//dispatch這個事件對象,重點,下面詳述
    Alert.show("刪除成功!");//彈出“刪除成功”對話框,沒難度
}	
        這段代碼裏就兩行有問題,1、定義一個新的事件對象,2、然後dispatch出去(類似於廣播模式,我在廣播裏喊一聲,大家聽着,誰的娃掉了,來撿)。

        第一個問題:定義新的事件對象,語法格式沒問題,new一個對象,關鍵是參數,第一個參數表示該事件的類型,第二個參數表示該事件是否允許參與冒泡流程,第一個名稱沒問題,反正是自定義個類型名稱而已,第二個什麼叫冒泡流程,簡單的理解就是池塘底的淤泥裏發酵產生一個氣泡,然後往上升,升啊升,一級級往上,突然中間出現一個喫泡泡的東西把它喫掉,ok,問題解決。

        第二個問題:什麼叫dispatch出去,這個就要和冒泡關聯起來了,就是如果允許冒泡,那麼就會出出來上面說的那個喫泡泡的東西,如果不允許,打死它都不出現,問題就解決不了。

        普通青年這樣理解:【參與冒泡模式】我創建了一個事件對象,但是目前我這個級別處理不了,然後我就把這個事件告訴其他模塊,快來幫我處理快來幫我處理,這是某個模塊挺好心的一致在關注我,然後他就發現了並處理掉了,ok,問題解決。

【不參與冒泡模式】我創建了一個事件,但是目前我這個級別處理不了,但是我覺得當前應用裏其他模塊應該會幫我處理就沒去請求幫助,然後這個事件就永遠擱置了。

        文藝青年這樣理解:【參與冒泡模式】我是郭敬明,有個氫氣球,沒抓牢,往上飄,我大喊我的氣球飛了,邊上有個姚明,他抓住了,還給我,問題解決。【不參與冒泡模式】我是郭敬明,有個氫氣球,沒抓牢,往上飄,我默默的看着邊上在忙的姚明,他沒理我,氣球飛了。

        2B青年這樣理解:【有獎徵集,回覆寫出答案,大家投票表決,前三名允許一次實驗補交,或屏蔽一次實驗借鑑。】

        有個要注意的地方是我這邊定義了一個名爲refreshDG的新事件類型,但是這個事件類型是我自定義的,系統不認,需要我們手動進行事件註冊,所以在當前文件的全局位置註冊下:

[Event(name="refreshDG", type="flash.events.Event")]
        好了我的新事件類型已經註冊、定義併發送出去了,但是誰來接收呢?我們這裏當然應該是主文件,不過你都說只是應該了,主文件並沒有這個義務來關注你一個小小的外部文件,所以我們必須和主文件打好關係,讓主文件時刻保持對我這個外部文件的自定義事件的監聽。因此在主文件裏我們需要添加一個事件監聽,加在什麼地方,當然是主文件的Application初始化的時候就來監聽了,也就是在creationComplete事件的處理函數裏:

this.addEventListener("refreshDG",refreshIT);
        這就表示我的主文件Application初始化之後就開始對refreshDG這個事件進行監聽,一旦這個事件被觸發,立刻調用refreshIT來處理。

        OK,事件篇就這樣結束了……是不是很Happy?Go to hell我想纔是大家的心聲……

        未完待續,下一篇【服務器端篇】



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章