天天看點

Android事件模型之interceptTouchEvnet ,onTouchEvent關系正解

參考文檔:

http://blog.csdn.net/liutao5757124/article/details/6097125

首先,看Android的官方文檔正解

onInterceptTouchEvent()與onTouchEvent()的機制:

  1. down事件首先會傳遞到onInterceptTouchEvent()方法

  2. 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return false,

  那麼後續的move, up等事件将繼續會先傳遞給該ViewGroup,之後才和down事件一樣傳遞給最

  終的目标view的onTouchEvent()處理

  3. 如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return true,

  那麼後續的move, up等事件将不再傳遞給onInterceptTouchEvent(),而是和down事件一樣

  傳遞給該ViewGroup的onTouchEvent()處理,注意,目标view将接收不到任何事件。

  4. 如果最終需要處理事件的view的onTouchEvent()傳回了false,那麼該事件将被傳遞至其上一

  層次的view的onTouchEvent()處理

  5. 如果最終需要處理事件的view 的onTouchEvent()傳回了true,那麼後續事件将可以繼續傳遞

  給該view的onTouchEvent()處理

這是官方文檔的說法,要是自己沒親自去寫個程式觀察哈,基本上沒法了解,是以上程式先,然後分析:

布局檔案main.xml

<?xml version="1.0" encoding="utf-8"?>
<com.hao.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<com.hao.LayoutView2
		android:orientation="vertical" android:layout_width="fill_parent"
		android:layout_height="fill_parent" android:gravity="center">
		<com.hao.MyTextView
			android:layout_width="wrap_content" android:layout_height="wrap_content"
			android:id="@+id/tv" android:text="AB" android:textSize="40sp"
			android:textStyle="bold" android:background="#FFFFFF"
			android:textColor="#0000FF" />
	</com.hao.LayoutView2>
</com.hao.LayoutView1> 

           

 第一層自定義布局LayoutView1.java

package com.hao;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class LayoutView1 extends LinearLayout {
	 private final String TAG = "LayoutView1"; 
     public LayoutView1(Context context, AttributeSet attrs) { 
    	 super(context, attrs); 
    	 Log.e(TAG,TAG); 
     }
     
     @Override 
     public boolean onInterceptTouchEvent(MotionEvent ev) { 
         int action = ev.getAction(); 
         switch(action){ 
         case MotionEvent.ACTION_DOWN: 
              Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN"); 
//              return true; 在這就攔截了,後面的就不會得到事件
              break;
		case MotionEvent.ACTION_MOVE: 
              Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE"); 
              break; 
         case MotionEvent.ACTION_UP: 
              Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP"); 
              break; 
         case MotionEvent.ACTION_CANCEL: 
              Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL"); 
              break; 
         } 
         return false; 
     } 

     @Override 
     public boolean onTouchEvent(MotionEvent ev) { 
         int action = ev.getAction(); 
         switch(action){ 
         case MotionEvent.ACTION_DOWN: 
              Log.e(TAG,"onTouchEvent action:ACTION_DOWN"); 
              break; 
         case MotionEvent.ACTION_MOVE: 
              Log.e(TAG,"onTouchEvent action:ACTION_MOVE"); 
              break; 
         case MotionEvent.ACTION_UP: 
              Log.e(TAG,"onTouchEvent action:ACTION_UP"); 
              break; 
         case MotionEvent.ACTION_CANCEL: 
              Log.e(TAG,"onTouchEvent action:ACTION_CANCEL"); 
              break; 
         } 
         return true; 
//         return false;
     } 
     
     @Override 
     protected void onLayout(boolean changed, int l, int t, int r, int b) { 
         // TODO Auto-generated method stub 
         super.onLayout(changed, l, t, r, b); 
     } 

     @Override 
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
         // TODO Auto-generated method stub 
         super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     } 
}
           

 第二層布局LayoutView2.java

package com.hao;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

public class LayoutView2 extends LinearLayout {
	
	private final String TAG = "LayoutView2"; 
    public LayoutView2(Context context, AttributeSet attrs) { 
       super(context, attrs); 
       Log.e(TAG,TAG); 
    } 
    
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
       int action = ev.getAction(); 
       switch(action){ 
       case MotionEvent.ACTION_DOWN: 
           Log.e(TAG,"onInterceptTouchEvent action:ACTION_DOWN");
//           return true;
           break; 
       case MotionEvent.ACTION_MOVE: 
           Log.e(TAG,"onInterceptTouchEvent action:ACTION_MOVE"); 
           break; 
       case MotionEvent.ACTION_UP: 
           Log.e(TAG,"onInterceptTouchEvent action:ACTION_UP"); 
           break; 
       case MotionEvent.ACTION_CANCEL: 
           Log.e(TAG,"onInterceptTouchEvent action:ACTION_CANCEL"); 
           break; 
       } 
       return false; 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
       int action = ev.getAction(); 
       switch(action){ 
       case MotionEvent.ACTION_DOWN: 
           Log.e(TAG,"onTouchEvent action:ACTION_DOWN"); 
           break; 
       case MotionEvent.ACTION_MOVE: 
           Log.e(TAG,"onTouchEvent action:ACTION_MOVE"); 
           break; 
       case MotionEvent.ACTION_UP: 
           Log.e(TAG,"onTouchEvent action:ACTION_UP"); 
           break; 
       case MotionEvent.ACTION_CANCEL: 
           Log.e(TAG,"onTouchEvent action:ACTION_CANCEL"); 
           break; 
       } 
//       return true; 
       return false;
    } 
}

           

 自定義MyTextView.java

package com.hao;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

public class MyTextView extends TextView {
	private final String TAG = "MyTextView"; 
    public MyTextView(Context context, AttributeSet attrs) { 
       super(context, attrs); 
       Log.e(TAG,TAG); 
    } 

    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
       int action = ev.getAction(); 
       switch(action){ 
       case MotionEvent.ACTION_DOWN: 
           Log.e(TAG,"onTouchEvent action:ACTION_DOWN"); 
           break; 
       case MotionEvent.ACTION_MOVE: 
           Log.e(TAG,"onTouchEvent action:ACTION_MOVE"); 
           break; 
       case MotionEvent.ACTION_UP: 
           Log.e(TAG,"onTouchEvent action:ACTION_UP"); 
           break; 
       case MotionEvent.ACTION_CANCEL: 
           Log.e(TAG,"onTouchEvent action:ACTION_CANCEL"); 
           break; 
       } 
       return false; 
//       return true;
    } 

    public void onClick(View v) { 
       Log.e(TAG, "onClick"); 
    } 

    public boolean onLongClick(View v) { 
       Log.e(TAG, "onLongClick"); 
       return false; 
    } 
}

           

 其實代碼很簡單,就是自定義了View,在View裡面都重寫了interceptTouchEvnet (),和onTouchEvent(),然後測試其傳回值,對監聽的影響,關鍵是自己動手,逐個測試,并預測結果,等你能預測結果的時候,也就懂了,需要修改的地方就是interceptTouchEvnet 和onTouchEvent的傳回值,他們決定了事件監聽的流程,下面我畫了一張圖,如有不足之處歡迎指正,謝謝!

下面是我的正解:

基本的規則是:        *1.down事件首先會傳遞到onInterceptTouchEvent()方法        *        * 2.如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return false(不攔截),       * 那麼後續的move, up等事件将繼續會先傳遞給該ViewGroup,之後才和down事件一樣傳遞給最終的目标view的onTouchEvent()處理。        *       * 3.如果該ViewGroup的onInterceptTouchEvent()在接收到down事件處理完成之後return true(攔截,那麼後面的move,up事件不需要在看因為已經攔截了, 我們直接拿去處理onTouchEvent()就可以了),那麼後續的move, up等事件将不再傳遞給onInterceptTouchEvent(), 而是和down事件一樣傳遞給該ViewGroup的onTouchEvent()處理,注意,目标view将接收不到任何事件。 下面例子示範:       * 1:LayoutView1(31375): onInterceptTouchEvent action:ACTION_DOWN       * 2:LayoutView2(31375): onInterceptTouchEvent action:ACTION_DOWN       * 3:LayoutView2(31375): onTouchEvent action:ACTION_DOWN       * 4:LayoutView1(31375): onInterceptTouchEvent action:ACTION_MOVE       * 5:LayoutView2(31375): onTouchEvent action:ACTION_MOVE       * 6:LayoutView1(31375): onInterceptTouchEvent action:ACTION_MOVE       * 7:LayoutView2(31375): onTouchEvent action:ACTION_MOVE       * 8:LayoutView1(31375): onInterceptTouchEvent action:ACTION_UP       * 9:LayoutView2(31375): onTouchEvent action:ACTION_UP       * 該設定為:       * onInterceptTouchEvent:LayoutView1為false,LayoutView2為true       * onTouchEvent:LayoutView2為true       * 故而事件在LayoutView2(onInterceptTouchEvent:傳回true)時被攔截并處理,根據上面說法就是LayoutView2後續的MOVE,UP操作都不在經過onInterceptTouchEvent,直接       * 交給onTouchEvent處理,結果也的确如此。(見:LayoutView2的3,5,7,9,第一次是onInterceptTouchEvent處理如1,以後交給onTouchEvent)       * 而LayoutView1都還是要經過onInterceptTouchEvent(見LayoutView1的4,6,8)       *        * 4.如果最終需要處理事件的view的onTouchEvent()傳回了false(沒能處理這個事件,不能丢在傳回來讓父繼續),       * 那麼該事件将被傳遞至其上一層次的view的onTouchEvent()處理。        * **************************************************************************       * 感覺像是一個圈,然後一直在找一個能處理這個消息的人,如果找到了就結束,沒找到就循環,直到回到發出消息的那個人       * 注(對下面):沒有标注的DOWN表示攔截事件onInterceptTouchEvent,标注了onTouchEvent就是處理事件       * a.如果都沒處理(onInterceptTouchEvent傳回false): A(DOWN)-->B(DOWN)-->C(onTouchEvent DOWN)-->B(onTouchEvent DOWN)-->A(onTouchEvent DOWN),沒有執行UP事件,注意有MOVE的話,在DOWN和UP之間,下面的都一樣。       *b. B處理(B的onInterceptTouchEvent傳回true): A(DOWN)-->B(DOWN)-->B(onTouchEvent)-->A(onTouchEvent UP)-->B(onTouchEvent UP)-->(over)       * 形象說明:如果父親不攔截消息就傳給兒子,如果兒子要這個消息就處理(DOWN),結束,然後有父親1--父親2--兒子以此釋放消息(UP)。 然是如果兒子對這個消息置之不理,那這個消息又傳回父親,由父親來處理即。

下面給出了5中情況(不攔截表示onInterceptTouchEvent傳回false):       * 11** 父親1(LayoutView1不攔截false)---父親2(LayoutView2不攔截false)--兒子(MyTextView,onTouchEvent return true)--結束       * 22** 父親1(LayoutView1不攔截false)---父親2(LayoutView2不攔截false)--兒子(MyTextView,onTouchEvent return false)--回傳給父親2(onTouchEvent return true)--結束       * 33** 父親1(LayoutView1不攔截false)---父親2(LayoutView2不攔截false)--兒子(MyTextView,onTouchEvent return false)--回傳給父親2(onTouchEvent return false)--父親1(onTouchEvent return true)--結束(如果都沒處理不在執行UP ACTION)       * 44** 父親1(LayoutView1攔截true)--父親1(onTouchEvent return true)--結束          (DOWN--DOWN(onTouchEvent)--UP(onTouchEvent))       * 55** 父親1(LayoutView1攔截false)--父親2(LayoutView2攔截true)--父親2(onTouchEvent return false)--父親1(onTouchEvent return true)--結束      (DOWN1--DOWN2--DOWN(2 onTouchEvent)--DOWN(1 onTouchEvent)--UP(1 onTouchEvent))(1:父親2,2:父親2)       *        * ***************************************************************************       * 5.如果最終需要處理事件的view 的onTouchEvent()傳回了true,那麼後續事件将可以繼續傳遞給該view的onTouchEvent()處理。        */

下面給出一張處理的流程圖:

Android事件模型之interceptTouchEvnet ,onTouchEvent關系正解

 下附源代碼:

繼續閱讀