Android事件分发理解

来源:互联网 发布:网络教育本科统考 编辑:程序博客网 时间:2024/05/27 21:47

Android事件分发机制是个难点和重点,结合下各家,写点自己的理解。。

首先抛出一个小问题,写一个button的点击事件

button.setOnClickListener(new OnClickListener() {      @Override      public void onClick(View v) {          Log.d("TAG", "onClick execute");  
再写一个button的touch事件

button.setOnTouchListener(new OnTouchListener() {      @Override      public boolean onTouch(View v, MotionEvent event) {          Log.d("TAG", "onTouch execute, action " + event.getAction());          return false;      }  });  
结果:

如果把ontouch方法返回值改为true,结果:


这是为什么?你先可以理解为返回true被消费了,不会往下传递了。

你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类里的dispatchTouchEvent方法,可是你会发现Button类里并没有这个方法,那么就到它的父类TextView里去找一找,你会发现TextView里也没有这个方法,那没办法了,只好继续在TextView的父类View里找一找,这个时候你终于在View里找到了这个方法,示意图如下:


然后我们来看一下View中dispatchTouchEvent方法的源码:

public boolean dispatchTouchEvent(MotionEvent event) {      if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&              mOnTouchListener.onTouch(this, event)) {          return true;      }      return onTouchEvent(event);  }  
看这个就明白了:刚才我们修改的outouch()方法return true,那么就走上面的if语句,被消费了没有执行click方法,如果outouch()ruturn false,那么dispatchTouchEvent()就ruturn onTouchEvent(event),说明了click方法一定在outouchEvent方法里面,点进去看下,果然
public boolean performClick() {      sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);      if (mOnClickListener != null) {          playSoundEffect(SoundEffectConstants.CLICK);          mOnClickListener.onClick(this);          return true;      }      return false;  }  
****************************************************************************************************************************
接下来我们看一个图

理解自:

注意:

  • 目前所有的图的事件是针对ACTION_DOWN的,对于ACTION_MOVE和ACTION_UP后面再说。

然后总结一下:1.有三层,Activity,ViewGroup,View, 然后呢,分别是dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent,理解为分发,拦截,消费,因为Activity和view在最上和最底层,所以没有onInterceptTouchEvent.

2,接下来认真看下图,有三种返回值,return true,return false, return super,如果只走return super的话那么就是一个U型,整个流程应该是Activity---->ViewGroup--->View 从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View--->ViewGroup--->Activity从下往上调用onTouchEvent方法。

只要分析dispatchTouchEvent它的四条线路就可以理解这幅图

3.线路1:dispatchTouchEvent和onTouchEvent只要是返回true就是被消费了,没了,不会往下传了

4.线路2:dispatchTouchEvent和onTouchEvent只要是返回false就会回溯给父空间的onTouchEvent处理,类似递归停止开始回溯。

5.dispatchTouchEvent返回super.XXXX()那么就会交给这个ViewGroup的onInterceptTouchEvent 来处理,onInterceptTouchEvent (意思拦截,就是ViewGrouo分发的时候,问一问要不要拦截,如果拦截返回true,如果不拦截返回false,默认return super.onInterceptTouchEvent()就是return false),这里面再分两条路,线路3:到了onInterceptTouchEvent 返回true表示拦截,那么意思说自己要处理,就传给自己的onTouchEvent处理,线路4:onInterceptTouchEvent 默认或者返回false那么就交给子view的dispatchTouchEvent

6.这个时候有人可能会有疑问,子view没有onInterceptTouchEvent ,那么怎么给它的onTouchEvent呢,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent



*************************************************************************************************************************8

关于ACTION_MOVE 和 ACTION_UP


上面讲解的都是针对ACTION_DOWN的事件传递,ACTION_MOVE和ACTION_UP在传递的过程中并不是和ACTION_DOWN 一样,
你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,
只有前一个事件(如ACTION_DOWN)返回true,才会收到ACTION_MOVE和ACTION_UP的事件。


对于ACTION_MOVE、ACTION_UP总结:ACTION_DOWN事件在哪个控件消费了(return true),
那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,
如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,如果ACTION_DOWN事件是在onTouchEvent消费的,
那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。



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 plain copy
  1. imageView.setOnTouchListener(new OnTouchListener() {  
  2.     @Override  
  3.     public boolean onTouch(View v, MotionEvent event) {  
  4.         Log.d("TAG""onTouch execute, action " + event.getAction());  
  5.         return false;  
  6.     }  
  7. });  

运行一下程序,点击ImageView,你会发现结果如下:




在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。


好了,关于View的事件分发,我想讲的东西全都在这里了。现在我们再来回顾一下开篇时提到的那三个问题,相信每个人都会有更深一层的理解。


1. onTouch和onTouchEvent有什么区别,又该如何使用?

从源码中可以看出,这两个方法都是在View的dispatchTouchEvent中调用的,onTouch优先于onTouchEvent执行。如果在onTouch方法中通过返回true将事件消费掉,onTouchEvent将不会再执行。


另外需要注意的是,onTouch能够得到执行需要两个前提条件,第一mOnTouchListener的值不能为空,第二当前点击的控件必须是enable的。因此如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现。


2. 为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?

如果你阅读了Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来 这篇文章。当时我在图片轮播器里使用Button,主要就是因为Button是可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其它action都能得到执行,才能实现图片滚动的效果。第二,在布局文件里面给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。


 理解自:http://www.jianshu.com/p/e99b5e8bd67b
http://blog.csdn.net/guolin_blog/article/details/9097463

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 不会开玩笑的人怎么办 螺钉滑牙了怎么办 手机接受信号不好怎么办 明天合格考了怎么办 和教育退订不了怎么办 舒张压降不下来怎么办 题目一行写不下怎么办 明细账写错了怎么办 生活太单调乏味怎么办 png图片不清晰怎么办 发票没写单位怎么办 右转车道直行了怎么办 注塑模具产品吸气怎么办 包装机理料线缺料不停怎么办 儿童小脑发育不好怎么办 普通税票弄坏了怎么办 肠胃不好人消瘦怎么办 子宫小发育不良怎么办 卵泡涨的慢怎么办 先天性子宫发育不良怎么办 签证出生日期错了怎么办 车启动开不动怎么办 etc显示无效卡怎么办 最小化只能保留一个怎么办 word句号在中间怎么办 搜狗输入法打不出字怎么办 台式电脑键盘打不出字怎么办 键盘挨锁了怎么办 名牌包过安检怎么办 和地铁安检员冲突怎么办 乘客不配合安检怎么办? 富人移民了穷人怎么办 感觉自己太帅怎么办 上海街头艺人证怎么办 婆婆死了带孩子怎么办 婆婆不带孩子怎么办 空腹吃李子了怎么办 唱歌不在调上怎么办 彩色衣服变黄怎么办 小学生学习态度不好怎么办 孕晚期血小板低怎么办