您的位置:首页 > 移动开发 > Android开发

Android开发之触摸事件处理机制详解

2014-10-09 11:30 591 查看

android触碰消息传递机制

用户的每次触碰(onClick,onLongClick,onScroll,etc.)都是由一个ACTION_DOWN+n个ACTION_MOVE+1个ACTION_UP组成的,用户触碰必先有个ACTION_DOWN响应,用户触碰结束必然会有个ACTION_UP。(当然如果在途中被拦截,就可能不会有了!)那么View是如何分发消息和拦截消息呢?

1.View及其子类都会有的两个方法:

public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent

public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

2.特殊的View子类ViewGroup则还有一个方法:

public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent

3.分发

dispatchTouchEvent 收到触碰,则向最外层的View传递消息,再向子层的View分发

4.拦截:

onInterceptTouchEvent 拦截返回true表示要拦截消息,不要再向子View传递(这里的子View不是继承关系,而是包容关系)。返回false则表示不拦截消息,可以继续向下一层级的View传递消息,子View将可以dispatchTouchEvent 收到触碰消息再分发消息

5.消息处理:

onTouchEvent 处理事件,拦截了消息,或者是最后一个收到消息的View调用此方法来处理事件,若返回true,则表示正确接收并处理。若返回false则表示没有被处理,将向父View传递(这里的父View不是继承关系,而是包容关系)

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

Java代码  


<span style="font-size: small;"><?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>   

</span>  

 第一层自定义布局LayoutView1.java

Java代码  


<span style="font-size: small;">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);   

     }   

}</span>  

 第二层布局LayoutView2.java

Java代码  


<span style="font-size: small;">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;  

    }   

}  

</span>  

 自定义MyTextView.java

Java代码  


<span style="font-size: small;">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;   

    }   

}  

</span>  

 其实代码很简单,就是自定义了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()处理。 
      */

下面给出一张处理的流程图:



 下附源代码:

TouchEventTest.rar (94.3 KB)
下载次数: 40
查看图片附件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息