Android 触摸响应处理逻辑

来源:互联网 发布:家庭预算软件 编辑:程序博客网 时间:2024/05/16 04:51

一、触摸响应逻辑概述

1.用户一个点击屏幕的动作会产生下面一系列触摸事件 :

    ACTION_DOWN->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP
也就是一个down和up事件再加上一些move事件,因为人的手指在按下抬起的过程中,肯定会存在一定距离的位移,也许非常小,但是程序会扑捉到这个位移,并认为是move事件。

2.android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
  1)public boolean dispatchTouchEvent(MotionEvent ev)  
  2)public boolean onInterceptTouchEvent(MotionEvent ev)
  3)public boolean onTouchEvent(MotionEvent ev) 

    onInterceptTouchEvent 是在ViewGroup里面定义的。Android中的layout布局类一般都是继承此类的。onInterceptTouchEvent是用于拦截手势事件的,每个手势事件都会先调用onInterceptTouchEvent。 
    onInterceptTouchEvent() 用于处理事件并改变事件的传递方向。返回值为false时事件会传递给子控件的onInterceptTouchEvent();返回值为true时事件会传递给当前控件的onTouchEvent(),而不在传递给子控件,这就是所谓的Intercept(截断)。
    onTouchEvent() 用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。可能你要问是否消费了又区别吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。

3.触摸事件处理流程
    一个Activity可能由多个view层叠而成(比如一个背景图上有一个按钮),所以触摸事件的处理是按照从下到上,再从上到下的逻辑来处理:

    1)触摸事件被驱动上报给android的wms(windowsmanagerservice);

    2)wms将这些触摸事件分发给当前活动的Activity;

    3)当前活动的Activity拿到事件,调用VIewRoot类的dispatchTouchEvent,给当前活动窗口的根view;

    4)根view按照从下到上的顺序调用他的dispatchTouchEvent方法把事件到子view;

    5)每个view有一个方法onInterceptTouchEvent,可以拦截触摸事件,返回true则不再继续向上传递触摸事件;

    6)分发到最上层的view后,会调用他的OnTouchEvent方法,然后按照从上到下的顺序调用下一层view的OnTouchEvent方法;

    7)如果某层的view的OnTouchEvent方法返回true,就中断传递触摸事件,不再调用下一层view的OnTouchEvent方法。

这样的触摸事件处理逻辑使得每一层的view都有机会响应触摸消息,非常的灵活。


二、示例说明

    还是拿一个Activity举例说明,如下图所示:

点击查看原始尺寸

附上xml文件:

  
  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>

   

这个activity里面从下到上的view有:LayoutView1、LayoutView2、MyTxtView

那么首先看一下默认的触屏事件的在onInterceptTouchEvent 、onTouchEvent两个函数之间的传递流程。如下图:

点击查看原始尺寸

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

点击查看原始尺寸

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

点击查看原始尺寸

以此类推,我们可以得到各种具体的情况,整个layout的view类层次中都有机会截获,而且能看出来外围的容器view具有优先截获权。

      当然,通常外围的layoutview1,layoutview2,只是布局的容器不需要响应触屏的点击事件,仅仅Mytextview需要相应点击。但这只是一般情况,一些特殊的布局可能外围容器也要响应,甚至不让里面的mytextview去响应。更有特殊的情况是,动态更换响应对象。


三、多层次动态处理触摸事件    

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

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

请输入图片描述

请输入图片描述

 

四、  运用

    前几天我需要实现一个功能:点击屏幕任意区域挂断通话并退出窗口,直接实现方法dispatchTouchEvent即可:

        @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
Log.d(TAG,"test-- monitor dispatchTouchEvent!");
int action = ev.getAction();
                switch (action) {
                     case MotionEvent.ACTION_DOWN:            
SystemServer.hangUp(CallId);
                                break;
                }
return super.dispatchTouchEvent(ev);
}

原创粉丝点击