首页 > 代码库 > Android事件分发机制详解(1)----探究View的事件分发
Android事件分发机制详解(1)----探究View的事件分发
探究View的事件分发
在Activity中,只有一个按钮,注册一个点击事件
[java] view plaincopy
- button.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.d("TAG", "onClick execute");
- }
- });
如果在需要一个触摸事件
[java] view plaincopy
- button.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.d("TAG", "onTouch execute, action " + event.getAction());
- return false;
- }
- });
onTouch事件的动作比onClick要多,onTouch有Action_down,Action_move,Action_up.如果都注册了,到底谁先执行.
可以看到,onTouch是先于onClick执行的.并且onTouch执行了两遍.因此事件的传递顺序是经过onTouch再到onClick的.
但细心点会发现,onTouch是有返回值的.如果将onTouch的返回值改成return true.则会出现
我们发现,onClick方法不再执行了!为什么会这样呢?你可以先理解成onTouch方法返回true就认为这个事件被onTouch消费掉了,因而不会再继续向下传递。
如果到现在为止,以上的所有知识点你都是清楚的,那么说明你对Android事件传递的基本用法应该是掌握了。不过别满足于现状,让我们从源码的角度分析一下,出现上述现象的原理是什么。
首先,无论摸到什么控件,一定会调用该控件的dispatchTouchEvent()方法,所以当我们点击按钮时,会调用Button的dispatchTouchEvent()方法,但是Button本身并没有这个方法,其父类TextView同样也没有,查看TextView的父类,会发现View中有.
首先,View的dispatchTouchEvent方法源码:
[java] view plaincopy
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
- mOnTouchListener.onTouch(this, event)) {
- return true;
- }
- return onTouchEvent(event);
- }
很清楚,如果满足三个表达式,则会返回true,否则进入onTouchEvent()方法
先看第一个条件,mOnTouchListener!=null 这里,只需要给控件注册了Touch事件,mOnTouchListener就一定不为空.
第二个条件,则是判断当前控件是否是enable的,因为是Button,所以默认都是enable的.
关键的是第三个条件,mOnTouchListener.onTouch(this,event), 如果在onTouch方法里面返回了true,则这个表达式成立,返回true,所以不会往下执行.因此onClick也不会被执行了.如果onTouch返回了false,则表达式会进入onTouchEvent()方法里.
上面的分析还透漏出了一个重要的信息,那就是onClick的调用肯定是在onTouchEvent(event)方法中的.
这样View的整个事件分发的流程就让我们搞清楚了,但还有一个重要知识点,就是touch事件的层级传递
我们如果给一个控件注册了touch事件,每次点击都会触发一系列的Action_Down,Action_Move,Action_Up等事件.如果在Action_Down中返回了false,则后面的一系列action动作就不会触发了.
简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。
说到这里,很多的朋友肯定要有巨大的疑问了。这不是在自相矛盾吗?前面的例子中,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到执行了吗?其实你只是被假象所迷惑了,让我们仔细分析一下,在前面的例子当中,我们到底返回的是什么。
参考着我们前面分析的源码,首先在onTouch事件里返回了false,就一定会进入到onTouchEvent方法中,然后我们来看一下onTouchEvent方法的细节。由于我们点击了按钮,就会进入到第14行这个if判断的内部,然后你会发现,不管当前的action是什么,最终都一定会走到第89行,返回一个true。
是不是有一种被欺骗的感觉?明明在onTouch事件里返回了false,系统还是在onTouchEvent方法中帮你返回了true。就因为这个原因,才使得前面的例子中ACTION_UP可以得到执行。
那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。如下所示:
[java] view plaincopy
- imageView.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.d("TAG", "onTouch execute, action " + event.getAction());
- return false;
- }
- });
在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。
好了,关于View的事件分发,我想讲的东西全都在这里了。现在我们再来回顾一下开篇时提到的那三个问题,相信每个人都会有更深一层的理解
来源: <http://blog.csdn.net/guolin_blog/article/details/9097463>
?onTouch与onTouchEvent的区别:
从源码看,两个方法都是在View的dispatchTouchEvent中调用的, onTouch是dispatchTouchEvent()方法中的一个条件,很大程度上决定是否消费掉这个事件.如果返回true消费掉事件,则不会进入onTouchEvent()方法中,因此更不会出现OnClick事件.如果返回false,则会进入onTouchEvent()方法.
需要注意的是,如果想要onTouch能够执行,必须满足mTouchListener不为空,以及当前点击的控件是enable的,否者,该控件不会响应onTouch事件,如果需要onTouch响应,则需要重写onTouchEvent()方法.
个人总结归纳:
- (一般情况下)事件传递的顺序先经过onTouch,在传递onClick.
- 如果onTouch返回true,则不会响应onClick方法.满足dispatchTouchEvent()方法的第三个表达式.返回true,不会进入onTouchEvent.
- onClick事件的调用是在onTouchEvent方法中.
- 控件的Touch事件,如果Action_Down结果返回false,则不会触发后面一系列action动作,但用Button却能触发,因为在onTouchEvent方法中最终会返回true.ImageView却不能,因为ImageView默认不可点击.
- 控件的Touch最开始会在dispatchTouchEvent()响应.
来自为知笔记(Wiz)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。