onInterceptTouchEvent和onTouchEvent调用关系详解

泡在网上的日子 / 文 发表于2012-11-19 22:43 次阅读

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

 

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

 

关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。

 

return true和return false,代表的是是否消费完该事件,也就是该事件是否会继续传递给下层或者上层组件继续处理。return true代表消费完不会继续传递,return false代表没有消费完将会继续传递。

如果没有onInterceptTouchEvent,只考虑onTouchEvent的话,比较容易分析和理解。假如有三层布局结构,linearLayout1,linearLayout2,textView,从前到后是包含的关系。那么下面分情况说明。
1.如果它们的onTouchEvent都返回false的话,DOWN事件会自上而下(textView位于最上层)依次传递,最终都没有消费完此事件,都只会进入onTouchEvent方法一次并且MotionEvent的action为MotionEvent.ACTION_DOWN,move和up等事件不会继续处理。

2.如果textView的onTouchEvent返回true,表示textView消费了此事件,不会传给父组件linearLayout2和linearLayout1了,并且还会继续处理move和up等事件。
3.linearLayout2和linearLayout1的onTouchEvent返回true和上面的情况一样,都不会继续传给父容器而且本身继续处理move和up等事件。
OK,这种情况还是比较容易理解的。

 

下面加入onInterceptTouchEvent。
onInterceptTouchEvent只有ViewGroup才会有,用于在进入自身onTouchEvent或者子组件onTouchEvent之前处理事件。注意onTouch是自上而下传递,而onInterceptTouch却是由下而上传递的。来了一个DOWN事件,首先进入的必然是最底层的viewGroup的onInterceptTouchEvent方法,然后根据return的值进入自身或者子组件的onTouch事件,当然如果子组件也是viewgroup的话,在进入子组件的onTouch之前也会进入子组件的onInterceptTouchEvent方法。
下面也分几种情况介绍:
1.当onInterceptTouchEvent返回false时,表示没有消费完此事件,会继续传递个子组件的onTouch继续处理。注意这种情况不会就不会传递给这个ViewGroup自身的onTouch事件处理了。这和onTouch如果返回false,后续的move、up等事件都不会继续处理了可以做同样理解。
2.当onInterceptTouchEvent返回true时,表示消费完此事件,或者说将在此组件上消费该事件。这种情况该事件会传递给ViewGroup自身的onTouch事件去处理,而不会传递给子组件的onTouch方法了。


由此可以总结,onInterceptTouchEvent返回值只是决定了是要把事件传递给自身的onTouch事件还是传递给子组件的onTouch事件。返回false表示没有消费完将传递给子组件的onTouch方法,返回true表示自身消费此事件,将传递给自身的onTouch方法而不会传递给子组件的onTouch方法了。

 

SDK给出的说明如下:

· You will receive the down event here.

· The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.

· For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target's onTouchEvent().

· If you return true from here, you will not receive any following events: the target view will receive the same event but with the actionACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.

由于onInterceptTouchEvent()的机制比较复杂,上面的说明写的也比较复杂,总结一下,基本的规则是:

  • 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()处理。

下面用一个简单的实验说明上述复杂的规则。视图自底向上共3层,其中LayoutView1和LayoutView2就是LinearLayout, MyTextView就是TextView:

对应的xml布局文件如下:

 

<?xml version="1.0" encoding="utf-8"?>   
      
<com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"
      
    android:orientation="vertical"
      
    android:layout_width="fill_parent"
      
    android:layout_height="fill_parent" >   
      
    <com.touchstudy.LayoutView2   
      
        android:orientation="vertical"
      
        android:layout_width="fill_parent"
      
        android:layout_height="fill_parent"
      
        android:gravity="center">   
      
       <com.touchstudy.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.touchstudy.LayoutView2>   
      
</com.touchstudy.LayoutView1>

下面看具体情况:

 

1. onInterceptTouchEvent()处理down事件均返回false,onTouchEvent()处理事件均返回true

------------------------------------------------------------------------------------------------------------------------------

04-11 03:58:42.620: DEBUG/LayoutView1(614): onInterceptTouchEvent action:ACTION_DOWN

04-11 03:58:42.620: DEBUG/LayoutView2(614): onInterceptTouchEvent action:ACTION_DOWN

04-11 03:58:42.620: DEBUG/MyTextView(614): onTouchEvent action:ACTION_DOWN

04-11 03:58:42.800: DEBUG/LayoutView1(614): onInterceptTouchEvent action:ACTION_MOVE

04-11 03:58:42.800: DEBUG/LayoutView2(614): onInterceptTouchEvent action:ACTION_MOVE

04-11 03:58:42.800: DEBUG/MyTextView(614): onTouchEvent action:ACTION_MOVE

…… //省略过多的ACTION_MOVE

04-11 03:58:43.130: DEBUG/LayoutView1(614): onInterceptTouchEvent action:ACTION_UP

04-11 03:58:43.130: DEBUG/LayoutView2(614): onInterceptTouchEvent action:ACTION_UP

04-11 03:58:43.150: DEBUG/MyTextView(614): onTouchEvent action:ACTION_UP

------------------------------------------------------------------------------------------------------------------------------

这是最常见的情况,onInterceptTouchEvent并没有做任何改变事件传递时序的操作,效果上和没有覆写该方法是一样的。可以看到,各种事件的传递本身是自底向上的,次序是:LayoutView1->LayoutView2->MyTextView。注意,在onInterceptTouchEvent均返回false时,LayoutView1和LayoutView2的onTouchEvent并不会收到事件,而是最终传递给了MyTextView。

 

2. LayoutView1的onInterceptTouchEvent()处理down事件返回true,

MyTextView的onTouchEvent()处理事件返回true

------------------------------------------------------------------------------------------------------------------------------

04-11 03:09:27.589: DEBUG/LayoutView1(446): onInterceptTouchEvent action:ACTION_DOWN

04-11 03:09:27.589: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_DOWN

04-11 03:09:27.629: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_MOVE

04-11 03:09:27.689: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_MOVE

…… //省略过多的ACTION_MOVE

04-11 03:09:27.959: DEBUG/LayoutView1(446): onTouchEvent action:ACTION_UP

------------------------------------------------------------------------------------------------------------------------------

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

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

MyTextView的onTouchEvent()处理事件返回false

LayoutView2的onTouchEvent()处理事件返回true

----------------------------------------------------------------------------------------------------------------------------

04-11 09:50:21.147: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_DOWN

04-11 09:50:21.147: DEBUG/LayoutView2(301): onInterceptTouchEvent action:ACTION_DOWN

04-11 09:50:21.147: DEBUG/MyTextView(301): onTouchEvent action:ACTION_DOWN

04-11 09:50:21.147: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_DOWN

04-11 09:50:21.176: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_MOVE

04-11 09:50:21.176: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_MOVE

04-11 09:50:21.206: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_MOVE

04-11 09:50:21.217: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_MOVE

…… //省略过多的ACTION_MOVE

04-11 09:50:21.486: DEBUG/LayoutView1(301): onInterceptTouchEvent action:ACTION_UP

        04-11 09:50:21.486: DEBUG/LayoutView2(301): onTouchEvent action:ACTION_UP

----------------------------------------------------------------------------------------------------------------------------

可以看到,由于MyTextView在onTouchEvent()中return false,down事件被传递给其父view,即LayoutView2的onTouchEvent()方法处理,由于在LayoutView2的onTouchEvent()中return true,所以down事件传递并没有上传到LayoutView1。注意,后续的move和up事件均被传递给LayoutView2的onTouchEvent()处理,而没有传递给MyTextView。

 

 

----------------------------------------------------------------------------------------------------------------

应大家的要求,我把源代码贴上,其实很简单,就是基础文件,主要是用来观察事件的传递。

主Activity: InterceptTouchStudyActivity.java:

public class InterceptTouchStudyActivity extends Activity {   
      
    static final String TAG = "ITSActivity";   
      
    TextView tv;   
      
         
      
    /** Called when the activity is first created. */
      
    @Override   
      
    public void onCreate(Bundle savedInstanceState) {   
      
        super.onCreate(savedInstanceState);   
      
        setContentView(R.layout.layers_touch_pass_test);   
      
     }   
      
      
 }

 LayoutView1.java:

public class LayoutView1 extends LinearLayout {   
      
      private final String TAG = "LayoutView1";   
      
        public LayoutView1(Context context, AttributeSet attrs) {   
      
      
         super(context, attrs);   
      
         Log.d(TAG,TAG);   
      
     }   
      
       
      
     @Override   
      
     public boolean onInterceptTouchEvent(MotionEvent ev) {   
      
         int action = ev.getAction();   
      
         switch(action){   
      
         case MotionEvent.ACTION_DOWN:   
      
              Log.d(TAG,"onInterceptTouchEvent action:ACTION_DOWN");   
      
//            return true;   
      
              break;   
      
         case MotionEvent.ACTION_MOVE:   
      
              Log.d(TAG,"onInterceptTouchEvent action:ACTION_MOVE");   
      
              break;   
      
         case MotionEvent.ACTION_UP:   
      
              Log.d(TAG,"onInterceptTouchEvent action:ACTION_UP");   
      
              break;   
      
         case MotionEvent.ACTION_CANCEL:   
      
              Log.d(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.d(TAG,"onTouchEvent action:ACTION_DOWN");   
      
              break;   
      
         case MotionEvent.ACTION_MOVE:   
      
              Log.d(TAG,"onTouchEvent action:ACTION_MOVE");   
      
              break;   
      
         case MotionEvent.ACTION_UP:   
      
              Log.d(TAG,"onTouchEvent action:ACTION_UP");   
      
              break;   
      
         case MotionEvent.ACTION_CANCEL:   
      
              Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");   
      
              break;   
      
         }   
      
              
      
         return true;   
      
     }   
      
       
      
     @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:

public class LayoutView2 extends LinearLayout {   
      
    private final String TAG = "LayoutView2";   
      
         
      
    public LayoutView2(Context context, AttributeSet attrs) {   
      
       super(context, attrs);   
      
       Log.d(TAG,TAG);   
      
    }   
      
       
      
    @Override   
      
    public boolean onInterceptTouchEvent(MotionEvent ev) {   
      
       int action = ev.getAction();   
      
       switch(action){   
      
       case MotionEvent.ACTION_DOWN:   
      
           Log.d(TAG,"onInterceptTouchEvent action:ACTION_DOWN");   
      
           break;   
      
       case MotionEvent.ACTION_MOVE:   
      
           Log.d(TAG,"onInterceptTouchEvent action:ACTION_MOVE");   
      
           break;   
      
       case MotionEvent.ACTION_UP:   
      
           Log.d(TAG,"onInterceptTouchEvent action:ACTION_UP");   
      
           break;   
      
       case MotionEvent.ACTION_CANCEL:   
      
           Log.d(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.d(TAG,"onTouchEvent action:ACTION_DOWN");   
      
           break;   
      
       case MotionEvent.ACTION_MOVE:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_MOVE");   
      
           break;   
      
       case MotionEvent.ACTION_UP:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_UP");   
      
           break;   
      
       case MotionEvent.ACTION_CANCEL:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");   
      
           break;   
      
       }   
      
            
      
       return true;   
      
    }    
      
}

MyTextView.java:

public class MyTextView extends TextView {   
      
    private final String TAG = "MyTextView";   
      
         
      
    public MyTextView(Context context, AttributeSet attrs) {   
      
       super(context, attrs);   
      
       Log.d(TAG,TAG);   
      
    }   
      
       
      
    @Override   
      
    public boolean onTouchEvent(MotionEvent ev) {   
      
       int action = ev.getAction();   
      
       switch(action){   
      
       case MotionEvent.ACTION_DOWN:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_DOWN");   
      
           break;   
      
       case MotionEvent.ACTION_MOVE:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_MOVE");   
      
           break;   
      
       case MotionEvent.ACTION_UP:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_UP");   
      
           break;   
      
       case MotionEvent.ACTION_CANCEL:   
      
           Log.d(TAG,"onTouchEvent action:ACTION_CANCEL");   
      
           break;   
      
       }   
      
            
      
       return false;   
      
    }   
      
         
      
    public void onClick(View v) {   
      
       Log.d(TAG, "onClick");   
      
    }   
      
         
      
    public boolean onLongClick(View v) {   
      
       Log.d(TAG, "onLongClick");   
      
       return false;   
      
    }   
      
}

 

 

 

收藏 赞 (2) 踩 (0)
上一篇:android自定义控件:坚直滚动的TextView
最近突然对原来做的一个项目有想法,当时是一个显示文本的界面会循环滚动,因为时间比较仓促,就以实现需求为目的写了一个滚动的TextView,结果还是效果挺好的。现在想把它分享给大家,这次写demo是从零开始,没在原来的项目基础上改,因为我发现原来的实现
下一篇:Android中滑屏初探 ---- scrollTo 以及 scrollBy方法使用说明
今天给大家介绍下Android中滑屏功能的一个基本实现过程以及原理初探,最后给大家重点讲解View视图中scrollTo 与 scrollBy 这两个函数的区别 。 首先 ,我们必须明白在 Android View视图是没有边界的,Canvas是没有边界的 ,只不过我们通过绘制特定的View时对