onInterceptTouchEvent和onTouchEvent调用时序

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

onInterceptTouchEvent()使用也很简单,若是在ViewGroup里覆写了该方法,那么就能够对各类touch事件加以拦截。可是如何拦截,是否全部的touch事件都须要拦截则是比较复杂的,touch事件在onInterceptTouchEvent()onTouchEvent以及各个childView间的传递机制彻底取决于onInterceptTouchEvent()onTouchEvent()的返回值。而且,针对down事件处理的返回值直接影响到后续moveup事件的接收和传递。  android

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

SDK给出的说明以下: ide

·  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(). this

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

 

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

1.       down事件首先会传递到onInterceptTouchEvent()方法 rest

2.       若是该ViewGrouponInterceptTouchEvent()在接收到down事件处理完成以后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,以后才和down事件同样传递给最终的目标viewonTouchEvent()处理。

3.       若是该ViewGrouponInterceptTouchEvent()在接收到down事件处理完成以后return true,那么后续的move, up等事件将再也不传递给onInterceptTouchEvent(),而是和down事件同样传递给该ViewGrouponTouchEvent()处理,注意,目标view将接收不到任何事件。

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

5.       若是最终须要处理事件的view onTouchEvent()返回了true,那么后续事件将能够继续传递给该viewonTouchEvent()处理。

 

下面用一个简单的实验说明上述复杂的规则。视图自底向上共3层,其中LayoutView1LayoutView2就是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事件均返回falseonTouchEvent()处理事件均返回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时,LayoutView1LayoutView2onTouchEvent并不会收到事件,而是最终传递给了MyTextView

 

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

MyTextViewonTouchEvent()处理事件返回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.       LayoutView1LayoutView2onInterceptTouchEvent()处理down事件返回false

MyTextViewonTouchEvent()处理事件返回false

LayoutView2onTouchEvent()处理事件返回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

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

能够看到,因为MyTextViewonTouchEvent()return falsedown事件被传递给其父view,即LayoutView2onTouchEvent()方法处理,因为在LayoutView2onTouchEvent()return true,因此down事件传递并无上传到LayoutView1注意,后续的moveup事件均被传递给LayoutView2onTouchEvent()处理,而没有传递给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;

    }

}

 

 

 

android官方文档有个标准解释,现摘录过来:
首先,看Android的官方文档正解

   
       
  1. onInterceptTouchEvent()与onTouchEvent()的机制: 
  2.   1. down事件首先会传递到onInterceptTouchEvent()方法 
  3.   2. 若是该ViewGrouponInterceptTouchEvent()在接收到down事件处理完成之return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,以后才和down事件同样传递给最终的目标viewonTouchEvent()处理 
  4.   3. 若是该ViewGrouponInterceptTouchEvent()在接收到down事件处理完成以后return true,那么后续的move, up等事件将再也不传递给onInterceptTouchEvent(),而是和down事件同样传递给该ViewGrouponTouchEvent()处理,注意,目标view将接收不到任何事件。 
  5.   4. 若是最终须要处理事件的viewonTouchEvent()返回了false,那么该事件将被传递至其上一层次的viewonTouchEvent()处理 
  6.   5. 若是最终须要处理事件的view onTouchEvent()返回了true,那么后续事件将能够继续传递给该viewonTouchEvent()处理。

仅仅看这个官方文档解释,就能理解清楚这两个函数关系以及用途的绝对是富有经验的framework高手。
不然,必定须要一个案例来阐释。假设咱们有这样一个layout,很是典型的

   
       
  1. <com.test.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"   
  2.     android:orientation="vertical" android:layout_width="fill_parent"   
  3.     android:layout_height="fill_parent">   
  4.     <com.test.LayoutView2   
  5.         android:orientation="vertical" android:layout_width="fill_parent"   
  6.         android:layout_height="fill_parent" android:gravity="center">   
  7.         <com.test.MyTextView   
  8.             android:layout_width="wrap_content"   android:layout_height="wrap_content"   
  9.       />   
  10.     </com.test.LayoutView2>   
  11. </com.test.LayoutView1>

用一个示例图来解释这个layout:

点击查看原始尺寸
一般外围的layoutview1,layoutview2,只是布局的容器不须要响应触屏的点击事件,仅仅Mytextview须要相应点击。但这只是通常状况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的状况是,动态更换响应对象。
那么首先看一下默认的触屏事件的在两个函数之间的传递流程。以下图:

点击查看原始尺寸

若是仅仅想让MyTextView来响应触屏事件,让MyTextView的OnTouchEvent返回true,那么事件流就变成以下图,能够看到layoutview1,layoutview2已经不能进入OnTouchEvent:

点击查看原始尺寸

另一种状况,就是外围容器想独自处理触屏事件,那么就应该在相应的onInterceptTouchEvent函数中返回true,表示要截获触屏事件,好比layoutview1做截获处理,处理流变成以下图:

点击查看原始尺寸

以此类推,咱们能够获得各类具体的状况,整个layout的view类层次中都有机会截获,并且能看出来外围的容器view具备优先截获权。

当咱们去作一些相对来说具备更复杂的触屏交互效果的应用时候,常常须要动态变动touch event的处理对象,好比launcher待机桌面和主菜单(见下图),从滑动屏幕开始到中止滑动过程中,只有外围的容器view才能够处理touch event,不然就会误点击上面的应用图标或者widget.反之在静止不动的状态下则须要可以响应图标(子view)的touch事件。摘取framework中abslistview代码以下

   
       
  1. public boolean onInterceptTouchEvent(MotionEvent ev) { 
  2.         int action = ev.getAction(); 
  3.  
  4.         switch (action & MotionEvent.ACTION_MASK) { 
  5.         case MotionEvent.ACTION_DOWN: { 
  6.   
  7.             if (touchMode == TOUCH_MODE_FLING) { 
  8.                 return true;  //fling状态,截获touch,由于在滑动状态,不让子view处理 
  9.             } 
  10.             break; 
  11.         } 
  12.  
  13.         case MotionEvent.ACTION_MOVE: { 
  14.             switch (mTouchMode) { 
  15.             case TOUCH_MODE_DOWN: 
  16.                 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 
  17.                 final int y = (int) ev.getY(pointerIndex); 
  18.                 if (startScrollIfNeeded(y - mMotionY)) { 
  19.                     return true;//开始滑动状态,截获touch事件,不让子view处理 
  20.                 } 
  21.                 break; 
  22.             } 
  23.             break; 
  24.         } 
  25. }
相关文章
相关标签/搜索