|
事件驅動觀念
基本觀念
Palm與許多其它的應用程式一樣, 是採用事件驅動的方式來撰寫程式, 如果你曾經用過VB, VFP, VC, Delphi, 你應該對事件驅動的觀念並不陌生, 因為這些程式都是採用事件驅動的方式來開發程式.
什麼是事件驅動呢? 簡單來說, 當外界對Palm作了任何的動作後, 都會透過硬體的插斷傳入一個訊號給作業系統Palm OS, 而OS收到訊息後, 就會產生一個事件. 當然了, 事件並不一定是完全由硬體觸發產生, 也有可能由軟體產生一個事件. 舉例來說, 當觸筆點到螢幕上時, 會有硬體插斷產生一個PenDown的事件, 而PenDown的事件, 交給作業系統後, 可能會另外產生memuDown事件.
透過事件的產生, 我們在程式中, 只要透過解析事件, 即可依據事件的狀態, 決定下一步動作. 這也就形成了程式開發的基礎.
事件迴圈
在傳統的程式中, 通常在程式啟動完成後, 都會加一行 read events的指令, 一般在高階語言中, 有時候並不真正知道這行程式的用意, 只知道要寫下此行程式, 軟體才能順利執行. 其實 read events是事件驅動程式裡面一個很重要的觀念, 它就是讓程式進入一個無窮迴圈裡面, 不斷地去偵側事件的產生, 然後再依據產生的事件, 去回應事件的動作.
迴圈裡面究道是作什麼事呢? 其實它就是一組 Do while ( Loop ) 的語法, 如果迴圈裡面, 不作任何處理, 則程式一旦進入, 就永遠不會出來, 因為沒有一個適當的出口給它. 當然了, 沒有人會這樣寫程式, 讓程式跳不出來, 所以我們會在迴圈裡面寫一個判斷式, 只要遇到某一種狀況, 就要離開此迴圈, 然後讓程式順利結束. 通常這種狀況是發生在, 當使用者按了某個結束鈕後, 由此鈕的程式中, 去產生一個結束事件, 然後在迴圈中的判斷式中, 寫到當某個事件值等於結束事件時, 就跳出迴圈.
Palm程式的寫法, 也不脫離這樣的觀念, 它也是在程式啟動完成後, 即進入無窮迴圈中, 並等待結束事件產生後, 則離開程式. 但由於Palm沒有結束程式的觀念, 因此它並不像Windows程式一樣, 要有一個結束按鈕, 或在功能表中有一個結束的項目, 用來離開程式. Palm本身因為是單工的作業系統, 因此一次只能作一件事, 所以無法開發多執行緒的程式, 所以它所謂的程式結束, 其實是只要其它的程式一開始, 就是上個程式的結束. 因此在事件迴圈中, 系統會自動偵測到結束事件, 而我們的程式因收到一個結束事件, 而跳出迴圈.
事件迴圈程式
static void AppEventLoop (void)
{ Word error;
EventType event;
do {
EvtGetEvent (&event, evtWaitForever);
PreprocessEvent (&event);
if (! SysHandleEvent (&event))
if (! MenuHandleEvent (NULL, &event, &error))
if (! ApplicationHandleEvent (&event))
FrmDispatchEvent (&event);
}
while (event.eType != appStopEvent);
}
對於每個 Palm 程式而言,它一定有一個 PilotMain 函式作為開始,如下範例:
DWord PilotMain( Word cmd, Ptr cmdPBP, Word launchFlags)
{
return StarterPilotMain(cmd, cmdPBP, launchFlags);
}
我們會在 PilotMain 函式裡面,呼叫一個我們自訂的 StarterPilotMain 函式,然後從 StarterPilotMain 函式開始進入我們的程式.究竟上述的 AppEventLoop 函式要寫在那裡呢?其實就是寫在 StarterPilotMain 函式中,而且是當它處理完了所有程式的起始動作後,然後呼叫此 AppEventLoop 函式,開始進入事件迴圈中.在 5-1 開始一個範例程式中,會有一個標準的範例程式範本.
接著,我們就來看看這個事件迴圈是如何處理所有的事件.
一開始的 do { ... } while (event.eType != appStopEvent); 動作,是表示不斷地重覆執行迴圈,直到接收到一個appStopEvent事件,才結束,也就是迴圈的出口.什麼時候系統會產生此事件呢?一般是當你啟動另外一個程式的時候,appStopEvent事件會被觸發,然後你的程式也隨之結束.
一旦進入事件迴圈,第一件事就是透過 EvtGetEvent (&event, evtWaitForever); 抓取事件值,並放在 event 變數中,接下來進行 PreprocessEvent (&event); 這是作一些事件的準備動作,由你自己去完成,所以本函式可有可無,不一定需要.
接著連續執行下列動作,當其中一段不成立時,表示接收到的事件,在此函式中沒有或不需被處理,因此接著進行下一個函式來處理此事件.
if (! SysHandleEvent (&event))
if (! MenuHandleEvent (NULL, &event, &error))
if (! ApplicationHandleEvent (&event))
FrmDispatchEvent (&event);
我們會透過參數傳遞,將我們接收到的事件,傳入給事件處理函式來處理它.什麼是事件處理函式呢?凡是xxxHandleEvent( ) 種類的函式,我們統稱事件處理函式,意思是指本函式是要用來處理事件規則的函式.
SysHandledEvent( ) ; 處理系統預設事件,像是硬體方面的事件,如 Turn on , Turn off 等.
MenuHandleEvent( ) ; 處理目前功能表的事件,當你點到功能表位置時,產生的事件,交由它處理.
ApplicationHandleEvent( ) ; 當傳入的事件不是系統事件,也不是功能表事件時,則呼叫應用程式事件函式,交由它來處理事件.
FrmDispatchEvent( );將事件導引到 Form 的事件處理函式來處理.意思就是說,經過上面三個函式都無法處理的事件,則需要透過目前的 Form 事件處理函式,來處理它.本函式的作用,就是會呼叫目前的 Form 事件處理函式.
前三個事件處理函式都比較容易理解,最後一個 FrmDispatchEvent 就比較難懂.其實,簡單地說,前二個事件處理函式,主要是處理系統本身的訊息,我們不太需要管它,而第三個程式事件處理函式,就是我們自己要寫的.
第三個事件處理函式,我們稱它為應用程式事件處理函式,一個 Palm 程式僅需要一個應用程式事件處理函式即可,一般命名為 ApplicationHandleEvent( ).它的功能僅是很簡單地偵側到目前是那個 Form 被啟動,然後,用 FrmSetEventHandler( ) 來設定目前 Form 的事件處理函式,設定好了後,當執行到 FrmDispatchEvent( ) 函式時,它就會呼叫目前 Form 的事件處理函式.
來看看 ApplicationHandleEvent( ) 它的寫法:
static Boolean ApplicationHandleEvent( EventPtr eventP)
{
Word formId;
FormPtr frmP;
if (eventP->eType == frmLoadEvent)
{
// 載入 Form 的資源號碼
formId = eventP->data.frmLoad.formID;
frmP = FrmInitForm(formId);
FrmSetActiveForm(frmP);
// 依據偵測到的 Form ID ,來決定設定目前 Form 的事件處理函式
switch (formId)
{
case MainForm:
// 將目前的 Form 事件處理函式,指向設為 MainFormHandleEvent( )
FrmSetEventHandler(frmP, MainFormHandleEvent);
break;
default:
break;
}
return true;
}
return false;
}
當 MainForm 被啟動時,會被 ApplicationHandleEvent( ) 抓到它的 FormID值,然後將本 Form 的事件處理函式設為 MainFormHandleEvent( ),也就是當 FrmDispatchEvent( ) 被執行時,會呼叫 MainFormHandleEvent( ) 函式,然後我們再將要處理的程式寫在 MainFormHandleEvent( ) 函式中.
再重覆一次,一個應用程式只需一個 ApplicationHandleEvent( ),然後看它需要幾個 Form,就有幾個 FormHandleEvent( ),也就是每個 Form 有它自己的事件處理函式,這是你應該注意的. |