ViewGroup事件传递机制
上一篇写了view的事件传递机制,这一片进阶一下,写一点关于ViewGroup的事件传递机制。
记住类似Button这种控件都是View的子类,类似布局这种控件都是ViewGroup的子类,而ViewGroup又是View的子类而已。
什么是ViewGroup?它和普通的View有什么区别?
答:ViewGroup就是一组View的集合,它包含很多的子View和子VewGroup,是Android中所有布局的父类或间接父类,像LinearLayout、RelativeLayout等都是继承自ViewGroup的。但ViewGroup实际上也是一个View,只不过比起View,它多了可以包含子View和定义布局参数的功能。ViewGroup继承结构示意图如下所示:
切记:ViewGroup是一个抽象类,View不是
可以看到,我们平时项目里经常用到的各种布局,全都属于ViewGroup的子类。
下边我们写一个例子:
首先我们来自定义一个布局,命名为MyLayout,继承自LinearLayout,如下所示:
然后,打开主布局文件activity_main.xml,在其中加入我们自定义的布局
可以看到,我们在MyLayout中添加了两个按钮,接着在MainActivity中为这两个按钮和MyLayout都注册了监听事件。
分别点击一下Button1、Button2和空白区域,打印结果如下所示
打印结果如下:
你会发现,当点击按钮的时候,MyLayout注册的onTouch方法并不会执行,只有点击空白区域的时候才会执行该方法。你可以先理解成Button的onClick方法将事件消费掉了,因此事件不会再继续向下传递。
那就说明Android中的touch事件是先传递到View,再传递到ViewGroup的吗?现在还不敢说,让我们再来做一个实验。
看源码,ViewGroup中有一个onInterceptTouchEvent方法,我们来看一下这个方法的源码:
只有一行代码,返回了一个false!
既然是布尔型的返回,那么只有两种可能,我们在MyLayout中重写这个方法,然后返回一个true试试,代码如下所示:
现在再次运行项目,然后分别Button1、Button2和空白区域,打印结果如下所示:
你会发现,不管你点击哪里,永远都只会触发MyLayout的touch事件了,按钮的点击事件完全被屏蔽掉了!这是为什么呢?如果Android中的touch事件是先传递到View,再传递到ViewGroup的,那么MyLayout又怎么可能屏蔽掉Button的点击事件呢?
搞清楚Android中ViewGroup的事件分发机制。
Android中touch事件的传递,绝对是先传递到ViewGroup,再传递到View的。上节View事件传递机制中有说明过,只要你触摸了任何控件,就一定会调用该控件的dispatchTouchEvent方法。实际情况是,当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。如果我们点击了MyLayout中的按钮,会先去调用MyLayout的dispatchTouchEvent方法,可是你会发现MyLayout中并没有这个方法。那就再到它的父类LinearLayout中找一找,发现也没有这个方法。那只好继续再找LinearLayout的父类ViewGroup,你终于在ViewGroup中看到了这个方法,按钮的dispatchTouchEvent方法就是在这里调用的。修改后的示意图如下所示:
图片参考工匠若水
ViewGroup中的dispatchTouchEvent方法的源码:
现在整个ViewGroup的事件分发流程的分析也就到此结束了,我们最后再来简单梳理一下吧。
1. Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。
2. 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
3. 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。