onTouchEvent和onInterceptTouchEvent详细研究

来源:互联网 发布:php 数组找重复的数据 编辑:程序博客网 时间:2024/04/28 01:39

重写控件的时候,总体来说有2种情况,一个是继承View机器子类(不包括ViewGroup及其子类),如果需要处理手势,则重写View的onTouchEvent();另一个是继承ViewGroup及其子类的,如果需要处理手势,则需要重写onInterceptTouchEvent()和onTouchEvent()。

面试的时候曾被问过这两个方法的调用顺序,当时也只是知道onInterceptTouchEvent在前,具体的执行过程,相互的影响却不知道.今天写了一个小demo详细研究了一下这两个方法之间的关系.

首先上代码:

主activity:InterceptTouchStudyActivity

 

Java代码  收藏代码
  1. package com.touchstudy;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.widget.TextView;  
  6.   
  7. public class InterceptTouchStudyActivity extends Activity {  
  8.   
  9.     TextView tv;  
  10.   
  11.     public void onCreate(Bundle savedInstanceState) {  
  12.   
  13.         super.onCreate(savedInstanceState);  
  14.   
  15.         setContentView(R.layout.main);  
  16.   
  17.     }  
  18.   
  19. }  

 布局文件main.xml

 

Java代码  收藏代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <com.touchstudy.LayoutView2  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent"  
  10.         android:gravity="center"  
  11.         android:orientation="vertical" >  
  12.   
  13.         <com.touchstudy.MyTextView  
  14.             android:id="@+id/tv"  
  15.             android:layout_width="wrap_content"  
  16.             android:layout_height="wrap_content"  
  17.             android:background="#FFFFFF"  
  18.             android:text="AB"  
  19.             android:textColor="#0000FF"  
  20.             android:textSize="40sp"  
  21.             android:textStyle="bold" />  
  22.     </com.touchstudy.LayoutView2>  
  23.   
  24. </com.touchstudy.LayoutView1>  

 类LayoutView1

 

Java代码  收藏代码
  1. package com.touchstudy;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.LinearLayout;  
  8.   
  9. public class LayoutView1 extends LinearLayout {  
  10.   
  11.     private final String TAG = "LayoutView1";  
  12.   
  13.     public LayoutView1(Context context, AttributeSet attrs) {  
  14.   
  15.         super(context, attrs);  
  16.   
  17.         Log.d(TAG, TAG);  
  18.   
  19.     }  
  20.   
  21.     @Override  
  22.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  23.   
  24.         int action = ev.getAction();  
  25.   
  26.         switch (action) {  
  27.   
  28.             case MotionEvent.ACTION_DOWN:  
  29.   
  30.                 Log.d(TAG, "onInterceptTouchEvent1 action:ACTION_DOWN");  
  31.   
  32.                 // return true;  
  33.   
  34.                 break;  
  35.   
  36.             case MotionEvent.ACTION_MOVE:  
  37.   
  38.                 Log.d(TAG, "onInterceptTouchEvent1 action:ACTION_MOVE");  
  39.   
  40.                 break;  
  41.   
  42.             case MotionEvent.ACTION_UP:  
  43.   
  44.                 Log.d(TAG, "onInterceptTouchEvent1 action:ACTION_UP");  
  45.   
  46.                 break;  
  47.   
  48.             case MotionEvent.ACTION_CANCEL:  
  49.   
  50.                 Log.d(TAG, "onInterceptTouchEvent1 action:ACTION_CANCEL");  
  51.   
  52.                 break;  
  53.   
  54.         }  
  55.         boolean b = false;  
  56.         Log.d(TAG, "onInterceptTouchEvent1 return:"+b);  
  57.         return b;  
  58.   
  59.     }  
  60.   
  61.     @Override  
  62.     public boolean onTouchEvent(MotionEvent ev) {  
  63.   
  64.         int action = ev.getAction();  
  65.   
  66.         switch (action) {  
  67.   
  68.             case MotionEvent.ACTION_DOWN:  
  69.   
  70.                 Log.d(TAG, "onTouchEvent1 action:ACTION_DOWN");  
  71.   
  72.                 break;  
  73.   
  74.             case MotionEvent.ACTION_MOVE:  
  75.   
  76.                 Log.d(TAG, "onTouchEvent1 action:ACTION_MOVE");  
  77.   
  78.                 break;  
  79.   
  80.             case MotionEvent.ACTION_UP:  
  81.   
  82.                 Log.d(TAG, "onTouchEvent1 action:ACTION_UP");  
  83.   
  84.                 break;  
  85.   
  86.             case MotionEvent.ACTION_CANCEL:  
  87.   
  88.                 Log.d(TAG, "onTouchEvent1 action:ACTION_CANCEL");  
  89.   
  90.                 break;  
  91.   
  92.         }  
  93.         boolean b = false;  
  94.         Log.d(TAG, "onTouchEvent1 return:"+b);  
  95.         return b;  
  96.   
  97.     }  
  98.   
  99.     @Override  
  100.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  101.   
  102.         // TODO Auto-generated method stub  
  103.   
  104.         super.onLayout(changed, l, t, r, b);  
  105.   
  106.     }  
  107.   
  108.     @Override  
  109.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  110.   
  111.         // TODO Auto-generated method stub  
  112.   
  113.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  114.   
  115.     }  
  116.   
  117. }  

 类LayoutView2

 

Java代码  收藏代码
  1. package com.touchstudy;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.widget.LinearLayout;  
  8.   
  9. public class LayoutView2 extends LinearLayout {  
  10.   
  11.     private final String TAG = "LayoutView2";  
  12.   
  13.     public LayoutView2(Context context, AttributeSet attrs) {  
  14.   
  15.         super(context, attrs);  
  16.   
  17.         Log.d(TAG, TAG);  
  18.   
  19.     }  
  20.   
  21.     @Override  
  22.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  23.   
  24.         int action = ev.getAction();  
  25.   
  26.         switch (action) {  
  27.   
  28.             case MotionEvent.ACTION_DOWN:  
  29.   
  30.                 Log.d(TAG, "onInterceptTouchEvent2 action:ACTION_DOWN");  
  31.   
  32.                 break;  
  33.   
  34.             case MotionEvent.ACTION_MOVE:  
  35.   
  36.                 Log.d(TAG, "onInterceptTouchEvent2 action:ACTION_MOVE");  
  37.   
  38.                 break;  
  39.   
  40.             case MotionEvent.ACTION_UP:  
  41.   
  42.                 Log.d(TAG, "onInterceptTouchEvent2 action:ACTION_UP");  
  43.   
  44.                 break;  
  45.   
  46.             case MotionEvent.ACTION_CANCEL:  
  47.   
  48.                 Log.d(TAG, "onInterceptTouchEvent2 action:ACTION_CANCEL");  
  49.   
  50.                 break;  
  51.   
  52.         }  
  53.         boolean b = false;  
  54.         Log.d(TAG, "onInterceptTouchEvent2 return:"+b);  
  55.         return b;  
  56.   
  57.     }  
  58.   
  59.     @Override  
  60.     public boolean onTouchEvent(MotionEvent ev) {  
  61.   
  62.         int action = ev.getAction();  
  63.   
  64.         switch (action) {  
  65.   
  66.             case MotionEvent.ACTION_DOWN:  
  67.   
  68.                 Log.d(TAG, "onTouchEvent2 action:ACTION_DOWN");  
  69.   
  70.                 break;  
  71.   
  72.             case MotionEvent.ACTION_MOVE:  
  73.   
  74.                 Log.d(TAG, "onTouchEvent2 action:ACTION_MOVE");  
  75.   
  76.                 break;  
  77.   
  78.             case MotionEvent.ACTION_UP:  
  79.   
  80.                 Log.d(TAG, "onTouchEvent2 action:ACTION_UP");  
  81.   
  82.                 break;  
  83.   
  84.             case MotionEvent.ACTION_CANCEL:  
  85.   
  86.                 Log.d(TAG, "onTouchEvent2 action:ACTION_CANCEL");  
  87.   
  88.                 break;  
  89.   
  90.         }  
  91.           
  92.         boolean b = false;  
  93.         Log.d(TAG, "onTouchEvent2 return:"+b);  
  94.         return b;  
  95.     }  
  96.   
  97. }  

 类MyTextView

 

Java代码  收藏代码
  1. package com.touchstudy;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.widget.TextView;  
  9.   
  10. public class MyTextView extends TextView {  
  11.   
  12.     private final String TAG = "MyTextView";  
  13.   
  14.     public MyTextView(Context context, AttributeSet attrs) {  
  15.   
  16.         super(context, attrs);  
  17.   
  18.         Log.d(TAG, TAG);  
  19.   
  20.     }  
  21.   
  22.     @Override  
  23.     public boolean onTouchEvent(MotionEvent ev) {  
  24.   
  25.         int action = ev.getAction();  
  26.   
  27.         switch (action) {  
  28.   
  29.             case MotionEvent.ACTION_DOWN:  
  30.   
  31.                 Log.d(TAG, "onTouchEvent_TextView action:ACTION_DOWN");  
  32.   
  33.                 break;  
  34.   
  35.             case MotionEvent.ACTION_MOVE:  
  36.   
  37.                 Log.d(TAG, "onTouchEvent_TextView action:ACTION_MOVE");  
  38.   
  39.                 break;  
  40.   
  41.             case MotionEvent.ACTION_UP:  
  42.   
  43.                 Log.d(TAG, "onTouchEvent_TextView action:ACTION_UP");  
  44.   
  45.                 break;  
  46.   
  47.             case MotionEvent.ACTION_CANCEL:  
  48.   
  49.                 Log.d(TAG, "onTouchEvent_TextView action:ACTION_CANCEL");  
  50.   
  51.                 break;  
  52.   
  53.         }  
  54.         boolean b = false;  
  55.         Log.d(TAG, "onTouchEvent_TextView return:"+b);  
  56.         return b;  
  57.   
  58.     }  
  59.   
  60.     public void onClick(View v) {  
  61.   
  62.         Log.d(TAG, "onClick");  
  63.   
  64.     }  
  65.   
  66.     public boolean onLongClick(View v) {  
  67.   
  68.         Log.d(TAG, "onLongClick");  
  69.   
  70.         return false;  
  71.   
  72.     }  
  73.   
  74. }  

onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截,由于ViewGroup会包含若干childView,因此需要能够统一监控各种touch事件的机会,因此纯粹的不能包含子view的控件是没有这个方法的,如LinearLayout就有,TextView就没有。

关于返回值的问题,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理,需要说明的是,如果 onTouchEvent针对down事件返回了false,那么之后的move和up事件即使没有被拦截,也是接收不到的,或者说识别不了.

onInterceptTouchEvent()使用也很简单,如果在ViewGroup里覆写了该方法,那么就可以对各种touch事件加以拦截。但是如何拦截,是否所有的touch事件都需要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()和onTouchEvent以及各个childView间的传递机制完全取决于onInterceptTouchEvent()和onTouchEvent()的返回值。并且,针对down事件处理的返回值直接影响到后续move和up事件的接收和传递。

总结一下,基本的规则是:

1. 对于一个事件,如果没有被任何view拦截的话(所有方法都返回false),全程的顺序是

LayoutView1->LayoutView2->MyTextView依次调用onInterceptTouchEvent()

然后MyTextView->LayoutView2->LayoutView1依次调用onTouchEvent()

2. onInterceptTouchEvent()只负责拦截不拦截,onTouchEvent()只负责处理不处理,两者只要返回true,该事件就停止向后传递.

3. 如果该ViewGrouponInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGrouponTouchEvent()处理.

4. 如果最终需要处理事件的viewonTouchEvent()返回了false,那么该事件将被传递至其上一层次的viewonTouchEvent()处理。

5. 如果最终需要处理事件的view onTouchEvent()返回了true,则上一层次的view收不到该事件,且后续事件将可以继续传递给该viewonTouchEvent()处理. 

 

下面开始在代码中试验.

1.onInterceptTouchEvent()处理事件均返回falseonTouchEvent()处理事件均返回true



 这是最常见的情况,onInterceptTouchEvent并没有做任何改变事件传递时序的操作,效果上和没有覆写该方法是一样的。可以看到,各种事件的传递本身是自底向上的,次序是:LayoutView1->LayoutView2->MyTextView。事件在MyTextView的OnTouch中被处理,停止了向父组件传递.

 

2.LayoutView1onInterceptTouchEvent()处理事件返回true,MyTextViewonTouchEvent()处理事件返回true


 由于LayoutView1在拦截第一次事件时return true,所以后续的事件(包括第一次的down)将由LayoutView1本身处理,事件不再传递下去。

 

3.LayoutView1,LayoutView2的onInterceptTouchEvent()处理事件返回false,MyTextView的onTouchEvent()处理事件返回false,LayoutView2的onTouchEvent()处理事件返回true



 由于MyTextViewonTouchEvent()return falsedown事件被传递给其父view,即LayoutView2onTouchEvent()方法处理,由于在LayoutView2onTouchEvent()return true,所以down事件传递并没有上传到LayoutView1注意,后续的moveup事件均被传递给LayoutView2onTouchEvent()处理,而没有传递给MyTextView