UI編程通常都會伴隨事件處理,Android也不例外,它提供了兩種方式的事件處理:基於回調的事件處理和基於監聽器的事件處理。
對於基於監聽器的事件處理而言,主要就是為Android介面組件綁定特定的事件監聽器;對於基於回調的事件處理而言,主要做法是重寫Android組件特定的回呼函數,Android大部分介面組件都提供了事件響應的回呼函數,我們主要重寫它們就行。
一 基於監聽器的事件處理
相比於基於回調的事件處理,這是更具“物件導向”性質的事件處理方式。在監聽器模型中,主要涉及三類對象:
1)事件來源Event Source:產生事件的來源,通常是各種組件,如按鈕,視窗等。
2)事件Event:事件封裝了介面組件上發生的特定事件的具體資訊,如果監聽器需要擷取介面組件上所發生事件的相關資訊,一般通過事件Event對象來傳遞。
3)事件監聽器Event Listener:負責監聽事件來源發生的事件,並對不同的事件做相應的處理。
基於監聽器的事件處理機制是一種委派式Delegation的事件處理方式,事件來源將整個事件委託給事件監聽器,由監聽器對事件進行響應處理。這種處理方式將事件來源和事件監聽器分離,有利於提供者的可維護性。
舉例:
View類中的OnLongClickListener監聽器定義如下:(不需要傳遞事件)
public interface OnLongClickListener {<br /> boolean onLongClick(View v);<br />}
View類中的OnLongClickListener監聽器定義如下:(需要傳遞事件MotionEvent)
public interface OnTouchListener {<br /> boolean onTouch(View v, MotionEvent event);<br />}
二 基於回調的事件處理
相比基於監聽器的事件處理模型,基於回調的事件處理模型要簡單些,該模型中,事件來源和事件監聽器是合一的,也就是說沒有獨立的事件監聽器存在。當使用者在GUI組件上觸發某事件時,由該組件自身特定的函數負責處理該事件。通常通過重寫Override組件類的事件處理函數實現事件的處理。
舉例:
View類實現了KeyEvent.Callback介面中的一系列回呼函數,因此,基於回調的事件處理機制通過自訂View來實現,自訂View時重寫這些事件處理方法即可。
public interface Callback {<br /> // 幾乎所有基於回調的事件處理函數都會返回一個boolean類型值,該傳回值用於<br /> // 標識該處理函數是否能完全處理該事件<br />// 返回true,表明該函數已完全處理該事件,該事件不會傳播出去<br />// 返回false,表明該函數未完全處理該事件,該事件會傳播出去<br /> boolean onKeyDown(int keyCode, KeyEvent event);<br /> boolean onKeyLongPress(int keyCode, KeyEvent event);<br /> boolean onKeyUp(int keyCode, KeyEvent event);<br /> boolean onKeyMultiple(int keyCode, int count, KeyEvent event);<br />}
三 比對
基於監聽器的事件模型符合單一職責原則,事件來源和事件監聽器分開實現;
Android的事件處理機制保證基於監聽器的事件處理會優先於基於回調的事件處理被觸發;
某些特定情況下,基於回調的事件處理機制會更好的提高程式的內聚性。
四 基於自訂監聽器的事件處理流程
在實際項目開發中,我們經常需要自訂監聽器來實現自訂商務程序的處理,而且一般都不是基於GUI介面作為事件來源的。這裡以常見的app自動更新為例進行說明,在自動更新過程中,會存在兩個狀態:下載中和下載完成,而我們的程式需要在這兩個狀態做不同的事情,“下載中”需要在UI介面上即時顯示軟體包下載的進度,“下載完成”後,取消進度條的顯示。這裡進行一個類比,重點在說明自訂監聽器的事件處理流程。
4.1)定義事件監聽器如下:
public interface DownloadListener {<br />public void onDownloading(int progress); //下載過程中的處理函數<br />public void onDownloaded();//下載完成的處理函數<br />}
4.2)實現下載操作的工具類代碼如下:
public class DownloadUtils {</p><p>private static DownloadUtils instance = null;</p><p>private DownloadUtils() {<br />}</p><p>public static synchronized DownloadUtils instance() {<br />if (instance == null) {<br />instance = new DownloadUtils();<br />}<br />return instance;<br />}</p><p>private boolean isDownloading = true;</p><p>private int progress = 0;</p><p>// 實際開發中這個函數需要傳入url作為參數,以擷取伺服器端的安裝包位置<br />public void download(DownloadListener listener) throws InterruptedException {<br />while (isDownloading) {<br />listener.onDownloading(progress);<br />// 下載過程的簡單類比<br />Thread.sleep(1000);<br />progress += 10;<br />if (progress >= 100) {<br />isDownloading = false;<br />}<br />}<br />// 下載完成<br />listener.onDownloaded();<br />}</p><p>}
4.3)最後在main函數中類比事件來源:
public class DownloadUI {</p><p>public static void main(String[] args) {<br />try {<br />DownloadUtils.instance().download(new MyDownloadListener());<br />} catch (InterruptedException e) {<br />e.printStackTrace();<br />}<br />}</p><p>private static class MyDownloadListener implements DownloadListener {</p><p>@Override<br />public void onDownloading(int progress) {<br />System.out.println("下載進度是:" + progress);<br />}</p><p>@Override<br />public void onDownloaded() {<br />System.out.println("下載完成");<br />}</p><p>} </p><p>}
運行上面的類比程式,輸出如下所示:
五 參考文獻
1)《瘋狂Android講義》