標籤:font com 手機 tools boolean ontouch roi nbsp context
前段時間剛接觸過android手機開發。對它的事件傳播機制不是非常瞭解,儘管網上也查了相關的資料,可是總認為理解模模糊糊,似是而非,於是自己就寫個小demo測試了一下。
總算搞明確了它的詳細機制。寫下自己的結論。分享之,希望對初學android的人有所協助
布局效果所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />
圖1
參照先說說詳細得到的結論:
1) onInterceptTouchEvent負責對touch事件進行攔截。對於嵌套的view最先啟動並執行是事件攔截方法的是最外層的那個view的onInterceptTouchEvent方法,然後依次運行子視圖的onInterceptTouchEvent,然後在運行子視圖的子視圖的事件攔截方法(當然在這裡假設全部嵌套視圖的onInterceptTouchEvent都會得到運行,讓每一個視圖的onInterceptTouchEvent返回false就可以)。參照,所以onInterceptTouchEvent運行順序就是A--->B--->C--->D.也就是由父視圖到子視圖傳遞。
總之。事件攔截機制是由父視圖開始發起對事件的攔截(出事了老子先上,兒子稍後)。
參照當手指觸摸事件時。父視圖A首先發起對該起事件的攔截。假設A攔截失敗,就交給它的子視圖B進行攔截;假設B攔截失敗就交給B的子視圖C再進行攔截..直到某一子視圖對該次事件攔截成功。
2)某一視圖攔截事件成功與否的推斷標識是onInterceptTouchEvent方法的傳回值,當返回true的時候說明攔截成功。返回false的時候說明當前視圖對事件攔截失敗。
3)以下說說攔截成功的情況,假設C視圖對當前touch事件攔截成功。攔截成功意味著此次事件不會再傳遞到D視圖了。
所以此時的D視圖的onInterceptTouchEvent就得不到運行(事件沒法到達了,還攔截誰呢?)。事件攔截成功後。緊接著就會對事件進行處理,處理的方法教給onTouchEvent方法處理。此時C視圖攔截成功,那麼緊接著就會運行C視圖的onTouchEvent方法,這是不是就意味著當前touch事件是由C視圖的onTouchEvent方法來處理的呢?這要由C視圖的onTouchEvent方法的傳回值來決定。當C視圖的onTouchEvent返回true的時候,當前事件就由C全權處理,處理的當然是事件的各種action,什麼MotionEvent.ACTION_MOVE,ACTION_UP都交給了C的onTouchEvent方法進行處理。所以此時就能夠在C的onTouchEvent方法中進行switch(event.getAction)推斷運行相關邏輯了。
假設返回的false,說明C視圖對此事件不做處理或者處理不了,怎麼辦呢?兒子不行老爸來,於是事件就交到了B視圖的onTouchEvent方法中。相同B對此事件處理與否還是看B的onTouchEvent傳回值。詳細的解釋就跟C一樣了,不複多言。
4)在A B C D的onInterceptTouchEvent和onTouchEvent都返回false的情況下。方法啟動並執行順序依次為A.onInterceptTouchEvent-->B.onInterceptTouchEvent-->C.onInterceptTouchEvent-->D.touchEvent(最深的子視圖沒重寫onInterceptTouchEvent)-->C.touchEvent-->B.touchEvent-->A.touchEvent.也就是說攔截事件是父視圖優先有子視圖進行攔截,處理事件是子視圖優先父視圖進行處理。
總結:onInterceptTouchEvent負責對事件進行攔截,攔截成功後交給最先遇到onTouchEvent返回true的那個view進行處理。
結合事件來源程式碼分析事件處理機制的話,能夠閱讀《從原始碼角度解析android事件原理》
以下將要詳細解說上面結論是怎麼得出的,準備分兩部分進行一步步解說。假設上面說的看明確的話,以下的內容就不要看了,因為會非常囉嗦。
圖1的布局代碼例如以下所看到的:
<com.example.demo.AView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.demo.BView android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.demo.CView android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.demo.DView android:layout_width="match_parent" android:layout_height="match_parent" android:text="測試demo" /> </com.example.demo.CView> </com.example.demo.BView></com.example.demo.AView>
當中最後一個D是一個自己定義的TextView。與A B C三個View的差別就是D僅僅重寫了onTouchEvent方法。A B C 這三個自己定義控制項還重寫了onInterceptEvent方法。
D的代碼例如以下,A B C代碼基本上除了類名和輸出log不一樣外其餘的都一樣。所以為了降低這裡僅僅貼出當中的一個。
DView的代碼:
public class DView extends TextView{ private static String tag = "D";public DView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public DView(Context context, AttributeSet attrs) {super(context, attrs);}public DView(Context context) {super(context);} @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e(tag, "--onTouchEvent--D");return false;}}
AView的代碼和C D的總體幾乎相同,就貼出來一個:
public class AView extends RelativeLayout{ private static String tag = "A";public AView(Context context) {super(context);}public AView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public AView(Context context, AttributeSet attrs) {super(context, attrs);} @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.e(tag,"--onInterceptTouchEvent--A");return false;}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e(tag,"--onTouchEvent---A" );return false;}}
剛開始的時候重寫的方法全部返回false運行點擊的效果輸出log為:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
轉換成為:
從此圖能夠看出,onInterceptTouchEvent事件的運行順序是由父控制項到子控制項。而且優先於自己控制項的onTouchEvent方法運行,onTouchEvent事件啟動並執行順序正好相反由子控制項到父控制項。注意因為此時都返回了false,是沒有哪一個view來處理此次的touch事件的各個ACTION的,這也是為什麼onTouchEvent為什麼會一直傳遞到A的原因。所以ACTION_MOVE和ACTION_UP等事件得不到對應(處理),此種情況下即使你在D的onTouchEvent方法裡面寫了例如以下代碼,也不會得到運行。
if(event.getAction()==MotionEvent.ACTION_MOVE){Log.e(tag, "--onTouchEvent--*****");}
1)假設A的InterceptTouchEvent返回了true,其餘的仍然返回false,那麼運行輸出的log為:
轉換成為:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
能夠發現此時A攔截了此次Touch事件。事件不再向A的子控制項B C D傳遞。此時全部的action事件比方手指移動事件ACTION_MOVE或者ACTION_UP事件啦等等事件都交給A的onTouchEvent方法去處理(當然這是在onTouchEvent方法返回true的情況下,假設返回false經過測試時不會對應這些action的)。B,C ,D控制項是的事件處理攔截方法和事件處理方法是無法得到啟動並執行。
2)僅僅有B的onIntercepteTouchEvent事件返回了true的情況下,列印的log為
轉換成為:
此時由B攔截了此次Touch事件,並不會向C D子控制項傳遞;相同的因為onTouchEvent事件返回為false,所以此次事件的event.getAction()的各種action都不會得到處理。
4)同理可知,C控制項的onIntercept方法返回了true的情況下。其餘的仍然返回false的情況下,輸出log為
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
轉換成為
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
以下說說各個view的onTouchEvent返回true的情況
因為onTouchEvent事件是從子控制項到父控制項傳遞的。當D的onTouchEvent返回true的時候,經測試輸出效果例如以下
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
轉換成為:
經過測試發現。此時D處理了此次Touch事件的各種action,C B D是的onTouchEvent的沒有得到運行。
同理當C的onTouchEvent方法返回了true的時候,輸出的log例如以下
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
轉換成例如以下:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2h1bnFpdXdlaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" />
經過測試發現,其餘情況一次類推,就不在囉嗦了。經過一步步的測試得出了文章開頭的結文章有點囉嗦,希望能夠對閱讀此文的人有所協助。
結合事件來源程式碼分析事件處理機制的話,能夠閱讀《從原始碼角度解析android事件原理》
android事件攔截處理機制具體解釋