天天看点

Android中事件传递机制的总结

事件传递虽然算不上某个单独的知识点,但是在实际项目开发中肯定会碰到,如果不明白其中的原理,那在设计各种滑动效果时就会感到很困惑。

关于事件的传递,我们可能会有以下疑问:

事件是如何传递的 事件是如何处理的 自定义view的时候,事件也冲突了怎么解决

带着这三个疑问,我们来总结一下事件传递机制是怎么回事。

一、事件分发的原理:

1、事件是如何传递的:

(1)首先由activity分发,分发给根view,也就是decorview(decorview为整个window界面的最顶层view) (2)然后由根view分发到子的view

如下图所示:

Android中事件传递机制的总结

再来看下面这张图:(这张图是整个事件传递机制的核心)

Android中事件传递机制的总结

上图显示:

  在viewgroup中可以通过onintercepttouchevent方法对事件传递进行拦截。onintercepttouchevent方法:

    返回true代表不允许事件继续向子view传递,将会触发当前view的ontouchevent(),进行事件的消费;

    返回false代表不对事件进行拦截,事件可以传递给孩子

    默认返回false

2、事件是如何处理的:

Android中事件传递机制的总结

再来看下面这张图:

Android中事件传递机制的总结

上图显示:子view中如果将传递的事件消费掉,父类的viewgroup中将无法接收到任何事件。

二、ontouch和onclick事件同时发生的问题:

首先这里要解释一下各种概念,避免混淆。

1、各种概念:

事件:

  混合体(可能是点击事件也可能是触摸事件)。

触摸事件:

  按下、滑动和离开

点击事件:

  按下、停留一会儿和离开

触摸ontouch事件和点击onclick事件有什么关系?

(1)执行先后不一样。触摸事件先执行 (2)触摸事件返回值影响点击事件(前者影响后者,而后者不影响前者)

2、ontouch和onclick事件同时执行:

    如果按钮的ontouch和onclick方法同时执行,会有什么效果呢?我们通过代码来看一下:

Android中事件传递机制的总结
Android中事件传递机制的总结

上方代码中,按钮btn既包含了ontouch事件,也包含了onclick事件,现在运行程序,点击按钮,后台打印的日志如下:

Android中事件传递机制的总结

通过上方日志我们可以看到,ontouch事件是比onclick事件先执行的。

备注:这里提示一下,如果我们仅仅只是用手指点击按钮,然后马上松开,ontouch事件中只会执行action_down和action_up动作;如果用手机点击按钮,并且手指还在按钮上滑动了一会儿,那么滑动的过程中,action_move动作就会不停的执行。现在我们应该能明白这三个动作的含义了吧?

3、只执行ontouch事件,不执行onclick事件:

如果按钮的ontouch和onclick方法同时执行,在有些情况下不太满足产品的需求。那如果只想执行ontouch事件,不执行onclick事件,该怎么做呢?很简单,只需要在上方代码中,将第39行的代码改为return true,就行了,即:将ontouch方法的返回值改为true,就会只执行ontouch事件,不执行onclick事件。改完代码之后,后台的运行效果如下:

Android中事件传递机制的总结

为什么这样改代码就可以了呢?这就需要从源码的角度来理解了。

button按钮中没有dispatchtouchevent方法,需要去它的父类view.java中去找dispatchtouchevent方法。源码如下所示:

Android中事件传递机制的总结

上图分析:红框部分的ontouch()方法默认是返回false,所以就会执行蓝框部分的代码,即:调用ontouchevent()方法。而ontouchevent方法中,会在action_up动作里面会去初始化onclick事件。如下图所示:

Android中事件传递机制的总结
Android中事件传递机制的总结

于是onclick事件就得到了执行。

三、onclick和onlongclick事件能同时发生:

我们通过代码来演示一下。

1、ontouch事件、onlongclick事件、onclick事件默认是同时执行:(执行的先后顺序:ontouch > onlongclick > onclick)

完整版代码如下:

Android中事件传递机制的总结
Android中事件传递机制的总结

运行程序后,长按按钮,后台日志如下:

Android中事件传递机制的总结

源码比较长,就不贴出来了,通过查看源码我们得知,当ontouch事件中的action_down动作执行180ms之后,就会执行onlongclick事件。

那我们现在知道了,如果在一个按钮上按下的时间过长,onlongclick事件会比onclick事件先执行。

2、只执行ontouch事件和onlongclick事件,不执行onclick事件:

为了实现这种逻辑,也很简单,只需要将上方的第50行代码改为return true就行了,即:将onlongclick方法的返回值改为true,就不会执行onclick事件了。改完代码之后,后台的运行效果如下:

Android中事件传递机制的总结

为什么这样改代码就可以了呢?这就需要从源码的角度来理解了。在view.java中的dispatchtouchevent方法里,action_up动作里面对onlongtouch事件进行了处理,具体源码就不展示出来了,这个有点复杂。

四、事件传递机制调用顺序:

viewgroup的事件传递方法:

dispatchtouchevent

onintercepttouchevent

ontouchevent

view的事件传递方法:

view的dispatchtouchevent

view的ontouchevent

注意,只有父的viewgroup容器才有onintercepttouchevent方法。这也很好理解,最小的那个子的view没必要再拦截了,因为无法继续向下传递事件,是否拦截已经没有意义了。

接下来,我们用linearlayout代表viewgroup,用button代表子view,然后去重写linearlayout和button中的事件传递方法,看一下各个方法的调用顺序。代码如下:

(1)mylinearlayout.java:(重写linearlayout中的事件传递方法)

Android中事件传递机制的总结
Android中事件传递机制的总结

(2)mybutton.java:(重写button中的事件传递方法,注意:这里面没有onintercepttouchevent方法)

Android中事件传递机制的总结
Android中事件传递机制的总结

上方代码中,将ontouchevent方法的返回值修改为true(59行),表示这个子的view希望消费这个事件。

(3)activity_main.xml:

Android中事件传递机制的总结
Android中事件传递机制的总结

<code>上面的xml中,将我们自定义的mylinearlayout和mybutton用上了。</code>

(4)mainactivity.java:

Android中事件传递机制的总结
Android中事件传递机制的总结

分析之前,我们先记住下面这句话:(记住这句话,分析下面的日志就好理解了)

  在android中,一切事件处理的开始都是从down事件开始的,如何你处理了down事件,其他的事件就都收不到了。

1、按照上面的代码,后台日志如下:

Android中事件传递机制的总结

通过上图的箭头处可以看到,事件是传递给了子view去消费。

2、上面的代码中,将mylinearlayout.java中ontouchevent方法返回值修改为true,将mybutton.java中的ontouchevent方法返回值修改为false,后台日志如下:

Android中事件传递机制的总结

通过上图的箭头处可以看到,事件是传递给了子view,子view说它不消费了,于是又回传给父的viewgroup,viewgroup消费了这个事件。

3、上面的代码中,将mylinearlayout.java中ontouchevent方法返回值修改为true,将mylinearlayout.java中onintercepttouchevent方法返回值修改为true,后台日志如下:

Android中事件传递机制的总结

通过上图的箭头处可以看到,此时viewgroup已经将事件拦截了,所以根本就不会传递给子的veiw,父的viewgroup自己把事件给消费掉了。

继续阅读