Android事件處理的兩種模型

UI編程通常都會伴隨事件處理,Android也不例外,它提供瞭兩種方式的事件處理:基於回調的事件處理和基於監聽器的事件處理。

對於基於監聽器的事件處理而言,主要就是為Android界面組件綁定特定的事件監聽器;對於基於回調的事件處理而言,主要做法是重寫Android組件特定的回調函數,Android大部分界面組件都提供瞭事件響應的回調函數,我們主要重寫它們就行。

 

一 基於監聽器的事件處理

相比於基於回調的事件處理,這是更具“面向對象”性質的事件處理方式。在監聽器模型中,主要涉及三類對象:

1)事件源Event Source:產生事件的來源,通常是各種組件,如按鈕,窗口等。

2)事件Event:事件封裝瞭界面組件上發生的特定事件的具體信息,如果監聽器需要獲取界面組件上所發生事件的相關信息,一般通過事件Event對象來傳遞。

3)事件監聽器Event Listener:負責監聽事件源發生的事件,並對不同的事件做相應的處理。

 

基於監聽器的事件處理機制是一種委派式Delegation的事件處理方式,事件源將整個事件委托給事件監聽器,由監聽器對事件進行響應處理。這種處理方式將事件源和事件監聽器分離,有利於提供程序的可維護性。

舉例:

View類中的OnLongClickListener監聽器定義如下:(不需要傳遞事件)

public interface OnLongClickListener { 
    boolean onLongClick(View v); 
}View類中的OnLongClickListener監聽器定義如下:(需要傳遞事件MotionEvent)

public interface OnTouchListener { 
    boolean onTouch(View v, MotionEvent event); 
}

二 基於回調的事件處理

相比基於監聽器的事件處理模型,基於回調的事件處理模型要簡單些,該模型中,事件源和事件監聽器是合一的,也就是說沒有獨立的事件監聽器存在。當用戶在GUI組件上觸發某事件時,由該組件自身特定的函數負責處理該事件。通常通過重寫Override組件類的事件處理函數實現事件的處理。

舉例:

View類實現瞭KeyEvent.Callback接口中的一系列回調函數,因此,基於回調的事件處理機制通過自定義View來實現,自定義View時重寫這些事件處理方法即可。

public interface Callback { 
    // 幾乎所有基於回調的事件處理函數都會返回一個boolean類型值,該返回值用於 
    // 標識該處理函數是否能完全處理該事件 
// 返回true,表明該函數已完全處理該事件,該事件不會傳播出去 
// 返回false,表明該函數未完全處理該事件,該事件會傳播出去 
       boolean onKeyDown(int keyCode, KeyEvent event); 
       boolean onKeyLongPress(int keyCode, KeyEvent event); 
       boolean onKeyUp(int keyCode, KeyEvent event); 
       boolean onKeyMultiple(int keyCode, int count, KeyEvent event); 
}

三 比對

基於監聽器的事件模型符合單一職責原則,事件源和事件監聽器分開實現;
某些特定情況下,基於回調的事件處理機制會更好的提高程序的內聚性。
Android的事件處理機制保證基於監聽器的事件處理會優先於基於回調的事件處理被觸發;

 

四 基於自定義監聽器的事件處理流程

在實際項目開發中,我們經常需要自定義監聽器來實現自定義業務流程的處理,而且一般都不是基於GUI界面作為事件源的。這裡以常見的app自動更新為例進行說明,在自動更新過程中,會存在兩個狀態:下載中和下載完成,而我們的程序需要在這兩個狀態做不同的事情,“下載中”需要在UI界面上實時顯示軟件包下載的進度,“下載完成”後,取消進度條的顯示。這裡進行一個模擬,重點在說明自定義監聽器的事件處理流程。

4.1)定義事件監聽器如下:

public interface DownloadListener { 
    public void onDownloading(int progress); //下載過程中的處理函數 
    public void onDownloaded(); //下載完成的處理函數 
}4.2)實現下載操作的工具類代碼如下:

public class DownloadUtils { 
        
    private static DownloadUtils instance = null; 
        
    private DownloadUtils() { 
    } 
        
    public static synchronized DownloadUtils instance() { 
        if (instance == null) { 
            instance = new DownloadUtils(); 
        } 
        return instance; 
    } 
        
    private boolean isDownloading = true; 
        
    private int progress = 0; // 實際開發中這個函數需要傳入url作為參數,以獲取服務器端的安裝包位置 
    public void download(DownloadListener listener) throws InterruptedException { 
        while (isDownloading) { 
            listener.onDownloading(progress); 
            // 下載過程的簡單模擬 
            Thread.sleep(1000); 
            progress += 10; 
            if (progress >= 100) { 
                isDownloading = false; 
            } 
        } 
        // 下載完成 
        listener.onDownloaded(); 
    } 
        
}4.3)最後在main函數中模擬事件源:

public class DownloadUI { 
           
    public static void main(String[] args) { 
        try { 
            DownloadUtils.instance().download(new MyDownloadListener()); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    }   private static class MyDownloadListener implements DownloadListener { 
       
        @Override
        public void onDownloading(int progress) { 
            System.out.println("下載進度是:" + progress); 
        } 
       
        @Override
        public void onDownloaded() { 
            System.out.println("下載完成"); 
        } 
               
    }  
       
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。