編輯:關於Android編程
任何程序都是靜態代碼,我們把這些靜態代碼打包好,然後放到運行環境當中,通過事件流的驅動使這些代碼運行起來。Android的環境也不例外。
靜態的代碼,在動態事件的驅動下,才會有效的運轉起來。
驅動Android程序運行起來的事件大致可以分為以下幾種:
用戶事件:如點擊屏幕,滑動等各種手勢;
系統事件:如屏幕方向的轉變;
線程通訊事件:線程之間互發消息,程序根據消息內容進行相應的響應;
進程通訊事件:這裡的進程包括本程序開啟的進程,也包括其他應用程序的進程。
而android程序探測事件是否發生的途徑,主要有兩類:監聽和回調。
監聽:
如設置一個監聽器,用來監聽某個控件是否被點擊了。監聽器可以是內部類的一個對象,外部類的一個對象(不常用,因為不能很好的獲得界面上對象的引用),也可以是activity本身。
回調:
寫一個回調方法即可,當某個事件發生時,系統就會調用這個方法。
對於基於監聽的處理模型來說,事件源與事件監聽是分離的,當事件源上發生特定事件時,該事件交給事件監聽器負責處理;對於基於回調的事件處理模型來說,事件源和事件監聽器是統一的,當事件源發生特定事件時,該事件還是由事件源本身負責處理。
下面從四種事件分類的角度具體探討:
用戶事件:
以button的點擊事件為例。從事件監聽器角度,可以設置內部類、外部類、匿名內部類作為事件監聽器,或者直接綁定標簽(在xml文件中button標簽下增加一行:android:onClick=“clickHandler”,然後在其對應的activity中實現clickHandler方法即可);從回調的角度,基於回調的事件處理機制可通過自定義view來實現,在自定義View中重寫該view的事件處理方法即可。
那麼問題來了,如果一個自定義button既自己重寫了事件處理方法,又設置了事件監聽器,那會怎麼執行呢?
答案是先執行事件監聽器的方法,再執行重寫的方法。為什麼會是這樣呢?這要從android的用戶事件的分發機制上來說了。
用戶事件的分發,從嚴格意義上來說,分為事件的分發和事件的處理。
事件的分發用於找到處理事件的對象,事件的處理用於找到事件的處理方式。
具體來說,用戶在界面的上觸發了某個操作,而界面上是有很多元素的,並且各個元素之間還是有層級關系的那麼最終這個操作事件交給哪個控件來處理呢?
我們先拋開用於監聽的事件,比如按鈕的點擊的監聽等,先把討論的范圍限定在可以回調的事件onTouchEvent,上邊已經把回調和監聽的區別說的很詳細了,我們不需要設定任何監聽對象就可以實現onTouchEvent的調用,只要在類中重寫onTouchEvent即可,當用戶發生touch操作的時候,就會自動調用這個方法。
好,我們先來看下下邊這張圖:
圖1
先來說說 onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent 這三個函數。activity中只有:dispatchTouchEvent、onTouchEvent。viewgroup中有:onInterceptTouchEvent、dispatchTouchEvent,因為viewgroup繼承於view,所以其實也有OnTouchEvent方法。
onInterceptTouchEvent 用來設定消息是否被阻斷,如果是,返回true,不是,返回false。
dispatchEvent用來向下層view或者viewgroup分發消息,如果下層 view或者viewgroup中的dispatchEvent方法都返回false,則調用自身的onTouchEvent方法(其實是調用的super.dispatchEvent方法,因為viewGroup的super是view,view的dispatchEvent方法實際上是調用的ontouch方法,所以最終調用的還是onTouchEvent方法)。
OnTouchEvent方法用於消費掉事件,及不讓事件繼續向後傳遞。(其實dispatchEvent也能消費事件,再在後邊的設置監聽的時候會講到)
view中的dispatchTouchEvent方法其實最終還是調用的onTouchEvent方法。如下圖源碼:
好了,干貨開始了。我剛才說了事件的處理實際上分為兩個階段,第一個階段是尋找可以處理事件的對象,第二個階段是處理事件的對象對事件進行處理。
現在說的是第一個階段。尋找處理事件的對象是通過獲取第一個響應down事件的控件進行的。如果某個控件響應的down事件,那麼之後的move和up事件都會由這個控件處理。那麼怎麼獲取第一個響應down事件的控件呢?
借助圖1來說,首先dalvik虛擬機講事件包裝成MotionEvent類,傳給當前activity的dispatchTouchEvent方法,然後activity分發給它的viewgroup對象,之後逐層調用dispatchtouchEvent方法向下分發,只要有一個onTouchEvent方法返回true,就停止分發操作(實際上是dispatchTouchEvent方法調用onTouchEvent方法,返回的true)。之後的所有操作(up 、move)就交由onTouchEvent返回true的那個控件處理。如果某個groupview調用所有子view的dispatchTouchEvent方法都返回false,他就調用自身的onTouchEvent方法。(實際上及調用了父類-view的dispatchTouchEvent方法,view的dispatchTouchEvent的方法)。如果onTouchEvent方法沒有重寫,因為onTouchEvent方法在view默認返回false,不會對事件流產生影響。重寫了OnTouchEvent方法但是返回flase,也是會捕獲下down事件,不會攔截後續的move和up事件。
總結下,拋開設置監聽事件不說,單從onTouchEvent的響應來看。如果想要在一個控件上攔截一個用戶事件,只需要重寫它的onTouchEvent方法,並且返回true即可。如果只是想獲取down的這個事件,並不攔截,只需要重寫onTouchEvent方法,但是返回false即可。如果一個viewGroup對事件進行了攔截(onInterceptTouchEvent返回了true,因為onInterceptTouchEvent在dispatchtouchEvent內部,所以dispatchTouchEvent就會終止向下分發這個事件,交由viewGroup的onTouchEvent處理down事件,和之後產生的move和up事件,如下圖源碼解析)
好,上邊是拋開了監聽來說的,下面來進一步說說有監聽的情況。
如果我給一個Button設置了click監聽,touch監聽,又重寫了onTouchEvent方法,會先執行哪個呢?
我們先從原理上來簡單解釋下,前邊講回調和監聽的原理的時候已經說過,監聽的優先級大於回調的優先級(這些是在代碼中控制的,待會兒會來分析源碼)。所以touch監聽肯定是比onTouchEvent要優先執行。通過下圖的源碼進行分析:
如果設置了touch監聽,該控件是enable的,touch對應的函數返回true,這個事件就會被這個監聽丟向攔截掉,不會再向後傳播了。touch對應的監聽函數返回了false,就還是會向後傳播的,touch只會捕獲到down的事件。
而onclick事件對應的監聽的執行,其實是在ontouch中的
performClick中是這樣的
所以說如果設置了onclick事件的捕獲,必須保證button控件成功攔截了該用戶事件,並且之後的捕獲了up事件。
如果重寫了onTouchEvent方法,這裡面對onclick處理的邏輯就會被覆蓋掉,所以設置了Onclick監聽也會失效的。
在ontouchEvent不重寫的前提下,無論設不設置onclick監聽,只要這個控件是可以響應onclick的,(如果setOnclickEnable(false)就不行了)onTouchEven都t會返回true,消費掉這個事件的。具體源碼實現,可以參考如下:
上如標出的部分是檢查是否是可以響應click事件(僅僅 是可不可以響應,至於有沒有設置click監聽,以及如何調用監聽對象的方法,就在switch中進行處理了)。如果為真,則最後OnTouchEvent都返回true如下圖:
總結一下,如果我們對一個控件同時設定了OnTouchListener、重寫OnTouchEvent、OnclickListener,執行順序是OnTouchListener、重寫的OnTouchEvent。或者OnTouchListener、OnclickListener.具體對源碼內容的解析,可以參看下圖:
關於系統事件、進程間通訊事件、線程間通訊事件將在後邊的文章中介紹。
在極客頭條上看到的方法,Mark一下,以後可能經常用到。1、打開android sdk manager2、打開tool->options,如圖所示https://&
版本:1.0 日期:2014.6.11 2014.6.12版權:© 2014 kince 轉載注明出處 ImageView是開發中經常使用到的一個控件,也可以
先占個位置,下次翻譯~ :p There are a few scenarios in which your activity is destroyed due t
顧名思義,裡面控件的位置都是相對的,定義好其中一個控件A的位置,然後其他的都可以相對於這個控件A或者是其已經與控件A建立相對位置關系的控件建立關系 盡量不要在相對管理裡牽