android事件攔截處理機制詳解

android事件攔截處理機制詳解

 

目錄(?)[+]

  1. 1如果A的InterceptTouchEvent返回了true其餘的仍然返回false那麼執行輸出的log爲

  2. 2只有B的onIntercepteTouchEvent事件返回了true的情況下打印的log爲

  3. 4同理可知C控件的onIntercept方法返回了true的情況下其餘的仍然返回false的情況下輸出log爲

  4. 下面說說各個view的onTouchEvent返回true的情況

  5. 同理當C的onTouchEvent方法返回了true的時候輸出的log如下

前段時間剛接觸過android手機開發,對它的事件傳播機制不是很瞭解,雖然網上也查了相關的資料,但是總覺得理解模模糊糊,似是而非,於是自己就寫個小demo測試了一下。總算搞明白了它的具體機制。寫下自己的結論,分享之,希望對初學android的人有所幫助

佈局效果如圖所示:

圖1

 

參照上圖先說說具體得到的結論:

1) onInterceptTouchEvent負責對touch事件進行攔截,對於嵌套的view最先執行的是事件攔截方法的是最外層的那個viewonInterceptTouchEvent方法,然後依次執行子視圖的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都交給了ConTouchEvent方法進行處理。所以此時就可以在ConTouchEvent方法中進行switch(event.getAction)判斷執行相關邏輯了。如果返回的false,說明C視圖對此事件不做處理或者處理不了,怎麼辦呢?兒子不行老爸來,於是事件就交到了B視圖的onTouchEvent方法中。同樣B對此事件處理與否還是看BonTouchEvent返回值,具體的解釋就跟C一樣了,不復多言。

4)A B C DonInterceptTouchEventonTouchEvent都返回false的情況下,方法執行的順序依次爲A.onInterceptTouchEvent-->B.onInterceptTouchEvent-->C.onInterceptTouchEvent-->D.touchEvent(最深的子視圖沒重寫onInterceptTouchEvent)-->C.touchEvent-->B.touchEvent-->A.touchEvent.也就是說攔截事件是父視圖優先有子視圖進行攔截,處理事件是子視圖優先父視圖進行處理。

總結:onInterceptTouchEvent負責對事件進行攔截,攔截成功後交給最先遇到onTouchEvent返回true的那個view進行處理

下面將要詳細講解上面結論是怎麼得出的,準備分兩部分進行一步步講解。如果上面說的看明白的話,下面的內容就不要看了,因爲會很囉嗦

圖1的佈局代碼如下所示:

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片

  1. <com.example.demo.AView xmlns:android="http://schemas.android.com/apk/res/android"

  2. xmlns:tools="http://schemas.android.com/tools"

  3. android:layout_width="match_parent"

  4. android:layout_height="match_parent" >

  5.  

  6. <com.example.demo.BView

  7. android:layout_width="match_parent"

  8. android:layout_height="match_parent" >

  9.  

  10. <com.example.demo.CView

  11. android:layout_width="match_parent"

  12. android:layout_height="match_parent" >

  13.  

  14. <com.example.demo.DView

  15. android:layout_width="match_parent"

  16. android:layout_height="match_parent"

  17. android:text="測試demo" />

  18. </com.example.demo.CView>

  19. </com.example.demo.BView>

  20.  

  21. </com.example.demo.AView>

<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的代碼:

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片

  1. public class DView extends TextView{

  2. private static String tag = "D";

  3. public DView(Context context, AttributeSet attrs, int defStyle) {

  4. super(context, attrs, defStyle);

  5. }

  6.  

  7. public DView(Context context, AttributeSet attrs) {

  8. super(context, attrs);

  9. }

  10.  

  11. public DView(Context context) {

  12. super(context);

  13. }

  14.  

  15. @Override

  16. public boolean onTouchEvent(MotionEvent event) {

  17. Log.e(tag, "--onTouchEvent--D");

  18. return false;

  19. }

  20. }

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);
	}
  
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.e(tag, "--onTouchEvent--D");	
		return false;
	}
}


AView的代碼和C D的整體差不多,就貼出來一個:

 

[java] view plaincopyprint?在CODE上查看代碼片派生到我的代碼片

  1. public class AView extends RelativeLayout{

  2. private static String tag = "A";

  3. public AView(Context context) {

  4. super(context);

  5. }

  6.  

  7. public AView(Context context, AttributeSet attrs, int defStyle) {

  8. super(context, attrs, defStyle);

  9. }

  10.  

  11. public AView(Context context, AttributeSet attrs) {

  12. super(context, attrs);

  13. }

  14.  

  15. @Override

  16. public boolean onInterceptTouchEvent(MotionEvent ev) {

  17. Log.e(tag,"--onInterceptTouchEvent--A");

  18. return false;

  19. }

  20.  

  21. @Override

  22. public boolean onTouchEvent(MotionEvent event) {

  23. Log.e(tag,"--onTouchEvent---A" );

  24. return false;

  25. }

  26. }

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);
	}
	 
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		Log.e(tag,"--onInterceptTouchEvent--A");
		return false;
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		Log.e(tag,"--onTouchEvent---A" );
		return false;
	}
}


 

剛開始的時候重寫的方法全部返回false運行點擊的效果輸出log爲:


轉換成效果圖爲:


 

從此圖可以看出,onInterceptTouchEvent事件的執行順序是由父控件到子控件,並且優先於自己控件的onTouchEvent方法執行,onTouchEvent事件執行的順序正好相反由子控件到父控件。注意由於此時都返回了false,是沒有哪一個view來處理此次的touch事件的各個ACTION的,這也是爲什麼onTouchEvent爲什麼會一直傳遞到A的原因。所以ACTION_MOVE和ACTION_UP等事件得不到相應(處理),此種情況下即使你在D的onTouchEvent方法裏面寫了如下代碼,也不會得到執行。

[java] view plaincopyprint?

  1. if(event.getAction()==MotionEvent.ACTION_MOVE){

  2. Log.e(tag, "--onTouchEvent--*****");

  3. }

if(event.getAction()==MotionEvent.ACTION_MOVE){
			Log.e(tag, "--onTouchEvent--*****");
		}

 

1)如果A的InterceptTouchEvent返回了true,其餘的仍然返回false,那麼執行輸出的log爲:

 

轉換成效果圖爲:



可以發現此時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爲

 

轉換成效果圖爲

下面說說各個view的onTouchEvent返回true的情況

由於onTouchEvent事件是從子控件到父控件傳遞的,當D的onTouchEvent返回true的時候,經測試輸出效果如下

轉換成效果圖爲:

經過測試發現,此時D處理了此次Touch事件的各種action,C B D是的onTouchEvent的沒有得到執行。

同理當C的onTouchEvent方法返回了true的時候,輸出的log如下

轉換成效果圖如下:

經過測試發現,此時事件的各個action都在CView的onTouchEvent方法得到了響應,而D的onTouchEvent是不會相應MotionEvent.ACTION_XX的。其餘情況一次類推,就不在囉嗦了。經過一步步的測試得出了文章開頭的結文章有點囉嗦,希望可以對閱讀此文的人有所幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章