Android事件分发分析(一)

来源:互联网 发布:邮箱服务器软件 编辑:程序博客网 时间:2024/06/06 19:54

强烈推荐大家先看一下我翻译的一篇关于事件分发的博客:事件的传递

我们先要明白三个概念

1.事件的分发    ---   dispatchTouchEvent

2.事件的拦截    ---  onInterceptTouchEvent 

3.事件的处理    ---  onTouchEvent


这是一个典型的Activity结构,Group_outer和Group_inner都是继承自RelativeLayout

Event_View继承自View,这算是我们的开胃小菜,分析一下原始情况下事件的分发,拦截和处理

对于Activity,我们暂且叫做EventActivity,重写了两个方法,如下   

  public boolean dispatchTouchEvent(MotionEvent ev) {        Log.d(TAG,"EventActivity开始分发事件");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d(TAG,"EventActivity开始处理事件");        return super.onTouchEvent(event);    }

对于Group_outer和Group_inner,我们重写了三个方法,如下  

@Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.d(EventActivity.TAG,"Group_outer开始分发事件");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d(EventActivity.TAG,"Group_outer开始处理事件");        return super.onTouchEvent(event);    }

对于EventView,我们重写了两个方法,如下    

  @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.d(EventActivity.TAG, "Event_view开始分发事件");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d(EventActivity.TAG,"Event_view开始处理事件");        return super.onTouchEvent(event);    }


1.当我们点击最里层的EventView时

打印日志如下:

06-16 15:33:25.382  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ EventActivity开始分发事件
06-16 15:33:25.382  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Group_outer开始分发事件
06-16 15:33:25.403  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Group_inner开始分发事件
06-16 15:33:25.403  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Event_view开始分发事件
06-16 15:33:25.403  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Event_view开始处理事件


06-16 15:33:25.432  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ EventActivity开始分发事件
06-16 15:33:25.432  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Group_outer开始分发事件
06-16 15:33:25.432  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Group_inner开始分发事件
06-16 15:33:25.432  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Event_view开始分发事件
06-16 15:33:25.432  22383-22383/com.example.administrator.eventhandlingtest D/eventhandlingtest﹕ Event_view开始处理事件

本人的分析如下:

首先,事件分发是非常,异常复杂的一个机制,但是我暂时只理解到这一块,抽象到自己能够接受,抓住了基本的步骤,便可以具体问题具体分析,然后再针对某一块深入研究,若是连基本概念都不知道,只会一直copy,内功是无法得到提高的(纯属个人意见)。

鼠标的行为是点击,我们做的是一个点击事件,没有任何拖泥带水(如犹豫的滑动),点击事件是一个复合事件。

它包含了两个基本事件,DOWN和UP,所以,一上一下就成了点击事件。根据日志,我们也可以知道系统处理了两次事件,即“按下”和“释放”.

当"按下"事件来的时候,系统所做的就是将这个事件分发下去,就像击鼓传花,它总得知道到底由谁来执行这个事件吧。说的抽象一点,你手触摸屏幕,屏幕拿到按下的事件,它需要把这个按下事件经过一系列的转换,变成我们代码体系中的一员,即我们所熟悉的View体系(水很深)。由于本人水平有限,我们就从Activity拿到事件开始说起吧。

根据日志,整个过程似乎很明朗了,分发-处理。

父亲首先拿到事件,在默认情况下(你没有重写任何相关方法的返回值),他会将这个事件一层层往下传,直到最底层的子孩子拿到这个事件,这个孩子将该事件传到它自己的onTouchEvent方法处理。所以,我们从日志中可以看到父亲仅仅是分发了事件,而孩子却真正处理了事件。

当然,这仅仅是最基本的概念,下面我们需要掌握我们所重写的三个方法的返回值对分发机制到底有什么影响,从而能在实际应用中灵活变通,实现想要的效果。

1.dispatchTouchEvent的返回值

       -return super.dispatchTouchEvent(ev);

        最终这个方法会走到ViewGroup中的dispatchTouchEvent(ev)

这段代码的作用就是,决定是否拦截这次事件,取决于两个因素,1.孩子是否希望你拦截 2.你自己是否希望拦截(很民主有没有)。代码里对应的两个标志位 disallowIntercept和intercepted,并且 1 的优先级高于 2,也就是说,如果孩子不希望你拦截,你自己想拦截也没什么鸟用,从代码里也可以看出。

我自己的困惑:子View到底在什么时候能够请求父亲不要拦截他的事件呢? ---这些问题会慢慢浮出水面,这是我自己思维的一个过程

在默认情况下,我们总是返回super.dispatchTouchEvent(ev),这个先放着,我们先从返回true开始

情景一: 假设Group_outter的dispatchTouchEvent返回true

我们在EventView上给了一个点击事件,日志如下

06-23 10:54:28.980: I/System.out(668): Activity开始分发事件
06-23 10:54:28.980: I/System.out(668): group_outer开始分发事件
06-23 10:54:29.082: I/System.out(668): Activity开始分发事件
06-23 10:54:29.082: I/System.out(668): group_outer开始分发事件

DOWN和UP事件既没有向下分发,也没有向上传递,而是在dispatchTouchEvent这个方法中被消费了

结论一     

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

情景二:  返回false

直接上日志,如下

06-23 11:13:40.364: I/System.out(716): Activity开始分发事件
06-23 11:13:40.369: I/System.out(716): group_outer开始分发事件
06-23 11:13:40.369: I/System.out(716): Activity处理事件
06-23 11:13:40.449: I/System.out(716): Activity开始分发事件
06-23 11:13:40.449: I/System.out(716): Activity处理事件

很明显,事件向上传递了,但是UP事件的处理有点奇怪,下面要引入一个关于dispatchTouchEvent 新的结论  

dispatchTouchEvent()方法中有“记忆”的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那就说明下面的View都没能成功处理该事件);当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,该View直接调用自己的onTouchEvent()方法来处理该事件。

注意:

“记忆”功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的   

结论二: 

如果 return false

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

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

情景三: 返回super.dispatchTouchEvent(ev)

06-23 11:23:56.650: I/System.out(811): Activity开始分发事件
06-23 11:23:56.650: I/System.out(811): group_outer开始分发事件
06-23 11:23:56.650: I/System.out(811): group_outer尝试截断事件
06-23 11:23:56.670: I/System.out(811): group_inner分发事件
06-23 11:23:56.670: I/System.out(811): group_inner尝试截断view的事件
06-23 11:23:56.670: I/System.out(811): view开始分发事件
06-23 11:23:56.680: I/System.out(811): view处理事件

06-23 11:23:56.700: I/System.out(811): Activity开始分发事件
06-23 11:23:56.700: I/System.out(811): group_outer开始分发事件
06-23 11:23:56.700: I/System.out(811): group_outer尝试截断事件
06-23 11:23:56.700: I/System.out(811): group_inner分发事件
06-23 11:23:56.700: I/System.out(811): group_inner尝试截断view的事件
06-23 11:23:56.700: I/System.out(811): view开始分发事件
06-23 11:23:56.700: I/System.out(811): view处理事件

事情似乎又回到的起点,只是多了一个截断事件,这个我在下一篇会详细讲到

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

如果大家觉得小弟的论述有些凌乱,还请谅解,我会尽量把我所知道的都说出来,这也是自己思路的一个整理





























0 0
原创粉丝点击