Android中的事件机制

来源:互联网 发布:java简单超市管理系统 编辑:程序博客网 时间:2024/04/30 13:27

Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)onInterceptTouchEvent(MotionEvent ev)onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroupViewActivity。方法与控件的对应关系如下表所示:

 

 

从这张表中我们可以看到 ViewGroup 和 View 对与 Touch 事件相关的三个方法均能响应,而 Activity 对 onInterceptTouchEvent(MotionEvent ev) 也就是事件拦截不进行响应。另外需要注意的是 View 对 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev) 的响应的前提是可以向该 View 中添加子 View,如果当前的 View 已经是一个最小的单元 View(比如 TextView),那么就无法向这个最小 View 中添加子 View,也就无法向子 View 进行事件的分发和拦截,所以它没有 dispatchTouchEvent(MotionEvent ev) 和 onInterceptTouchEvent(MotionEvent ev),只有 onTouchEvent(MotionEvent ev)

 

一、Touch 事件分析

事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下:

如果 return true,事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费,同时事件会停止向下传递;

如果 return false,事件分发分为两种情况:

如果当前 View 获取的事件直接来自 Activity,则会将事件返回给 Activity 的 onTouchEvent 进行消费;

如果当前 View 获取的事件来自外层父控件,则会将事件返回给父 View 的  onTouchEvent 进行消费。

如果返回系统默认的 super.dispatchTouchEvent(ev),事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。

事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下:

如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;

如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发;

如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),事件默认会被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。

事件响应:public boolean onTouchEvent(MotionEvent ev)

在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true 或返回 super.onInterceptTouchEvent(ev) 的情况下 onTouchEvent 会被调用。onTouchEvent 的事件响应逻辑如下:

如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回 false,这个事件就会“消失”,而且接收不到下一次事件。

如果返回了 true 则会接收并消费该事件。

如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。

到这里,与 Touch 事件相关的三个方法就分析完毕了。下面的内容会通过各种不同的的测试案例来验证上文中三个方法对事件的处理逻辑。

 

在其中对OntachEvent中的总结中,不是很具体。本文将主要对onTach进行总结。

 

ontachAndroid系统中整个事件机制的基础。Android中的其他事件,如onClickonLongClick等都是以onTach为基础的。

 

onTach包括从手指按下到离开手机屏幕的整个过程,在微观形式上,具体表现为action_downaction_moveaction_up等过程。

 

onTach两种主要定义形式如下:

 

1.在自定义控件中,常见的有重写onTouchEvent(MotionEvent ev)方法。如在开发中经常可以看到重写的onTouchEvent方法,

 

并且其中有针对不同的微观表现(action_downaction_moveaction_up等)做出的相应判断,执行逻辑并可能返回不同的布尔值。

 

2.在代码中,直接对现有控件设置setOnTouchListener监听器。并重写监听器的onTouch方法。onTouch回调函数中有viewMotionEvent

 

参数,据此也可以针对不同的事件微观表现作出不同的处理。

 

需要注意的是:整体上,事件的微观表现遵循”事件传递和消费“一文,但是针对于action_down,需要有如下注意的地方:

 

1.在所有的事件微观表现中,action_down是整个事件的基础,是任何宏观事件的起始事件,一旦action_down return false,表示事件继续向外层冒泡,当有某一层的action_down

 

return true,表示此层消费了此action_down事件,那么在接下里的action_moveaction_up等事件中,将直接先传入此层中,且不管action_move

 

action_up等返回false还是true,事件都不会继续冒泡到外层。事件由此被消费掉。

 

2.由此可以得知,action_down在整个事件传递中的重要作用。如果某层发生了action_move或者action_up微观事件,那么一定发发生过action_down微观事件。

 

 关于setOnTouchListenersetOnClickListenersetOnLongClickListener

 

Android中,有时候经常见到针对同一控件可能设置不同的事件监听器(如setOnTouchListenersetOnClickListenersetOnLongClickListener),对于这些事件监听器的执行顺序,

 

setOnTouchListener是最先执行的。并且只有当此空间完整走完action_downaction_up流程后,才可能调用performClick()方法,及调用onclick执行。而onLongClick则是在action_down

 

之后开始,并且是在一个新的线程中去判断按压的时间,条件满足则调用performLongClick()函数,及调用onLongClick()函数。

 

 

ButtononTouchonClickonLongClick事件发生先后顺序和关联: 

一,onTouch返回false 

首先是onTouch事件的down事件发生,此时,如果长按,触发onLongClick事件; 

然后是onTouch事件的up事件发生,up完毕,最后触发onClick事件。 

 

二,onTouch返回true 

首先是onTouch事件的down事件发生,然后是onTouch事件的up事件发生;期间不触发onClickonLongClick事件 

 

三,onTouchdown返回trueup返回false:结果同二。 

机制分析: 

   onTouch事件中:down事件返回值标记此次事件是否为点击事件(返回false,是点击事件;返回true,不记为点击事件),而up事件标记此次事件结束时间,也就是判断是否为长按。 

只要当down返回true时候,系统将不把本次事件记录为点击事件,也就不会触发onClick或者onLongClick事件了。因此尽管当up的时候返回false,系统也不会继续触发onClick事件了。 

 

四,onTouchdown返回falseup返回true: 

首先是onTouch事件的down事件发生,此时: 

长按,触发onLongClick事件,然后是onTouch事件的up事件发生,完毕。 

短按,先触发onTouchup事件, 到一定时间后,自动触发onLongClick事件。 

机制分析: 

  onTouch事件中:down事件返回值标记此次事件是否为点击事件(返回false,是点击事件;返回true,不记为点击事件),而up事件标记此次事件结束时间,也就是判断是否为长按。 

  当down返回false,标记此次事件为点击事件,而up返回了true,则表示此次事件一直没有结束,也就是一直长按下去了,达到长按临界时间后,自然触发长按事件,而onClick事件没有触发到。 

 

总结:

onLongClick发生只有在downfalseup为任意时发生;

Onclick发生在downfalseupfalse时发生,

 

下面是个例子: 

Java代码  收藏代码

class OnTouchListener implements View.OnTouchListener {  

  

@Override  

public boolean onTouch(View v, MotionEvent mEvent) {  

     

    switch (v.getId()) {  

    case R.id.XXXXX:  

    if(mEvent.getAction() == MotionEvent.ACTION_DOWN) {  

        selfDrive.setImageDrawable(getResources().getDrawable(R.drawable.a1));  

    } else {  

        selfDrive.setImageDrawable(getResources().getDrawable(R.drawable.a0));  

    }  

    return true;  

   default:  

     return true;  

     }  

  }  

  

  

 

 

 

 

 

 AndroidTouch事件处理机制比较复杂,特别是在考虑了多点触摸以及事件拦截之后。

 

      AndroidTouch事件处理分3个层面:Activity层,ViewGroup层,View层。

 

      首先说一下Touch事件处理的几条基本规则。

 

      1.如果在某个层级没有处理ACTION_DOWN事件,那么该层就再也收不到后续的Touch事件了直到下一次ACTION_DOWN事件。

 

         说明:a.某个层级没有处理某个事件指的是它以及它的子View都没有处理该事件。

 

                 b.这条规则不适用于Activity层(它是顶层),它们可以收到每一个Touch事件。

 

                 c.如果没有处理ACTION_MOVE这类事件,不会有任何影响。

 

      2.如果ACTION_DOWN事件发生在某个View的范围之内,则后续的ACTION_MOVEACTION_UPACTION_CANCEL等事件都将被发往该View,即使事件已经出界了。

 

      3.第一根按下的手指触发ACTION_DOWN事件,之后按下的手指触发ACTION_POINTER_DOWN事件,中间起来的手指触发ACTION_POINTER_UP事件,最后起来的手指触发ACTION_UP事件(即使它不是触发ACTION_DOWN事件的那根手指)。

 

      4.pointer id可以用于跟踪手指,从按下的那个时刻起pointer id生效,直至起来的那一刻失效,这之间维持不变。

 

      5.如果一个ACTION_DOWN事件被父View拦截了,则任何子View不会再收到任何Touch事件了(这符合第1点的要求)。

 

      6.如果一个非ACTION_DOWN事件被父View拦截了,则那些上次处理了ACTION_DOWN事件的子View会收到一个ACTION_CANCEL事件,之后不会再收到任何Touch事件了,即使父View不再拦截后续的Touch事件。

 

      7.如果父View决定处理Touch事件或者子View没有处理Touch事件,则父View按照普通View的处理方式处理Touch事件,否则它根本不处理Touch事件(它只负责分发)。

 

      8.如果父ViewonInterceptTouchEvent中拦截了事件,则onInterceptTouchEvent中不会再收到Touch事件了,事件被直接交给它自己处理(按照普通View的处理方式)。

 

      下面分层讲述一些细节。

 

      1.Activity层:

 

复制代码

public boolean dispatchTouchEvent(MotionEvent ev) {

        if (ev.getAction() == MotionEvent.ACTION_DOWN) {

            onUserInteraction();

        }

        if (getWindow().superDispatchTouchEvent(ev)) { //在这里交给View层处理

            return true;

        }

        return onTouchEvent(ev); // 如果View层没有处理,则在这里处理

}

复制代码

      2.View层:

 

复制代码

public boolean dispatchTouchEvent(MotionEvent event) {

         // 省略了部分细节

         ListenerInfo li = mListenerInfo;

         if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED

                    && li.mOnTouchListener.onTouch(this, event)) {

             return true;

         }

         if (onTouchEvent(event)) {

             return true;

         }

        return false;

}

复制代码

      ViewonTouch方法代码比较多,主要的逻辑分两步:先是将事件交给TouchDelegate处理(如果有的话),如果TouchDelegate没有处理再自行处理;自行处理主要负责View状态的变换(如按下状态),长按事件,点击事件的检测与触发等。

 

      3.ViewGroup层(比较复杂):

 

      ViewGroup层处理Touch事件的总体逻辑是:先检测是否需要拦截,没有拦截的话下发给子View处理,如果子View没有处理再自行处理,自行处理的逻辑与View一样。

 

      拦截的逻辑是,将从downup之间的所有事件看作一组事件,如果从down就拦截了,则组内的后续其它事件完全交给自己处理,不需要再进入拦截逻辑了;如果是从中间拦截,则先给子View发送cancel事件,组内的后续其它事件完全交给自己处理,不需要再进入拦截逻辑了。

 

      分发的逻辑是,在ACTION_DOWN事件的时候,寻找子View进行处理,称为寻找Target;如果没有找到Target,则自行处理;如果找到Target,则交由Target处理。

 

      从代码上看,dispatchTouchEvent负责分发逻辑,onTouchEvent负责真正的处理逻辑,一般应该重载onTouchEvent,只有特殊情况下才需要重载dispatchTouchEvent

0 0
原创粉丝点击