事件分发的流程:
Activity->window->view

例1:当我们对控件的onClick和onTouch事件进行注册时,点击控件两个方法都执行,且onTouch优先于onClick执行。当onTouch返回值为true时,表示这个触摸事件被onTouch消费了,此时不会再继续向下传递,onClick不执行。

1. 当触摸到控件,一定调用该控件View的dispatchTouchEvent():

public boolean dispatchTouchEvent(MotionEvent event) {if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&mOnTouchListener.onTouch(this, event)) {return true;}return onTouchEvent(event);

if 判断如果三个条件都为真,则返回true,否则执行onTouchEvent(event)。
条件为:
注册了监听器:mOnTouchListener 监听器不为空,当注册了touch事件监听器就不为空。
控件enable可用: 判断当前控件是否是可用的enable,按钮默认true。
onTouch()返回true:回调控件注册touch事件时的onTouch()方法,得到返回值。

所以dispatchTouchEvent() 首先执行onTouch(),如果注册touch的事件的onTouch()返回true,此时三个条件都满足,方法返回true,不向下执行,所以onClick()不执行。

2. 当三个条件不满足时,调用onTouchEvent()

public boolean onTouchEvent(MotionEvent event) {...//注2:如果该控件是可以点击的 ---> switch判断事件if (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {switch (event.getAction()) {//抬起手指case MotionEvent.ACTION_UP:...//经过层层判断if (!post(mPerformClick)) {//调用performClick()performClick();}...break;}return true;}return false;
}

onTouchEvent()中判断该控件是否可点击,如果可点击判断是ACTION_UP抬起事件,调用performClick()。

public boolean performClick() {...if (mOnClickListener != null) {...mOnClickListener.onClick(this);//注1return true;}return false;
}

只要mOnClickListener不为空,调用它的onClick()。
mOnClickListener在setOnListener时就赋值了,每当控件被点击,就会回调被点击控件的onClick()。

public void setOnClickListener(OnClickListener l) {...mOnClickListener = l;
}

3.讲讲touch事件的层级传递

当点击一个按钮,按道理会执行多个事件,ACTION_DOWN、ACTION_UP。
在dispatchTouchEvent的事件分发机制中,只有前一个事件返回true,下一个事件才会执行。 也就是如果按下按钮时,ACTION_DOWN返回false,后面一系列的action都不执行。

看performClick()注1位置,虽然点击事件在onTouch()返回false,但是在onTouchEvent()中会返回true。 所以dispatchTouchEvent()返回值还是true,action继续传递,而返回false时action会停止传递。

当换一个控件,比如imageView,给它注册一个touch触摸事件,控件默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。

***注:
只有dispatchTouchEvent(MotionEvent event)一直返回true,后续的事件才能执行。
让dispatchTouchEvent(MotionEvent event)返回ture有两种方式,第一:onTouch(this, event)返回true(这个时候不这行onTouchevent)。第二:如果onTouch为false,则只能通过onTouchEvent(event)返回true。

4.最后总结,回答三个问题

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

onTouch()优先执行,当onTouch()返回true()时表示消费事件,onTouchEvent()不执行。
onTouch()需要执行有两个前提条件:mOnTouchListener不能为空,控件是可点击enable的。非enable的onTouch事件永远不执行。
如果要监听非enable控件的touch事件,需要重写onTouchEvent()。

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

滑动菜单的功能是通过给ListView注册了一个touch事件来实现的。如果你在onTouch方法里处理完了滑动逻辑后返回true,那么ListView本身的滚动事件就被屏蔽了,自然也就无法滑动(原理同前面例子中按钮不能点击),因此解决办法就是在onTouch方法里返回false。

3)为什么图片轮播器里的图片使用Button而不用ImageView?

因为Button是可点击的,而ImageView是不可点击的。如果想要使用ImageView,可以有两种改法。
第一,在ImageView的onTouch方法里返回true,这样可以保证ACTION_DOWN之后的其它action都能得到执行,才能实现图片滚动的效果。
第二,在布局文件里面给ImageView增加一个android:clickable="true"的属性,这样ImageView变成可点击的之后,即使在onTouch里返回了false,ACTION_DOWN之后的其它action也是可以得到执行的。

***注:
button和imageview的区别是onTouchEvent中的注2部分的判断,
button可以进入这个判断,而imageview不能进入,只要进入这个判断,那么就是一定返回true,而不进入这个判断一定返回false。所以button的dispatchTouchEvent一直为true,一直可以接受到后续事件,然后在onTouch中处理,而imageview一旦onTouch中返回false,只能寄希望于上述判断,也就是将其设置为可点击,才能返回true,接受后续事件。

5. ViewGroup的事件分发流程

例2:当给父控件注册touch触摸监听,给子控件注册click点击监听,点击子控件,只会执行子控件onClick,不会执行父控件onTouch。
点击空白区域会执行父控件onTouch。

为啥呢?

ViewGroup中的onIntercepterTouchEvent()拦截触摸事件的方法,默认返回false不拦截事件:

public boolean onInterceptTouchEvent(MotionEvent ev) {return false;
}

当我们在父控件的此方法返回true,表示拦截事件后,点击子控件和空白区域都只执行父控件的onTouch。不管你点击哪里,永远都只会触发父控件的touch事件了,子控件的点击事件完全被屏蔽掉了。

Android中touch事件的传递,是先传递到ViewGroup,再传递到View的。
上面说过,只要你触摸控件,就一定会调用该控件的dispatchTouchEvent方法。这个说法没错,只不过还不完整而已。

实际情况是,当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。
如果我们点击了自定义的LinearLayout控件MyLayout中的按钮,会先去调用MyLayout的dispatchTouchEvent方法,可是你会发现MyLayout中并没有这个方法。那就再到它的父类LinearLayout中找一找,发现也没有这个方法。那只好继续再找LinearLayout的父类ViewGroup,你终于在ViewGroup中看到了这个方法,按钮的dispatchTouchEvent方法就是在这里调用的。

ViewGroup中的dispatchTouchEvent方法:

public boolean dispatchTouchEvent(MotionEvent ev) {...boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (action == MotionEvent.ACTION_DOWN) {.../当禁用掉事件拦截的功能  或 onInteerceptTouchEvent()关闭事件拦截 为false 时if (disallowIntercept || !onInterceptTouchEvent(ev)) {ev.setAction(MotionEvent.ACTION_DOWN);...//遍历所有子Viewfor (int i = count - 1; i >= 0; i--) {final View child = children[i];if ((child.mViewFlags & VISIBILITY_MASK) == VISIBL || child.getAnimation() != null) {child.getHitRect(frame);//判断这个子View是不是正被点击的if (frame.contains(scrolledXInt, scrolledYInt)) {//调用被点击子View的dispatchTouchEvent()处理点击事件if (child.dispatchTouchEvent(ev))  {mMotionTarget = child;//点击事件执行,返回true,结束方法,如一开始的结果,点击子控件只会执行子控件onClick,拦截掉父类的touch事件。return true;}}}}}}...//当点击空白区域,调用ViewGroup的父类View的dispatchTouchEvent(),同第一点,此时执行注册的onTouch。if (target == null) {...return super.dispatchTouchEvent(ev);}...return target.dispatchTouchEvent(ev);
}

ViewGroup事件分发的流程图:

在ViewGroup中可以对事件传递进行拦截,修改onInterceptTouchEvent的返回值,true为拦截自己处理,false不拦截继续传给子View。
若子View将传递的事件消费掉,ViewGroup无法接收任何事件。

6. View的事件分发流程

[1] 三个角色

Activity:只分发和消费
事件由ViewRootImpl中DecorView dispatchTouchEvent分发Touch事件–>Activity.dispatchTouchEvent…–>ViewGroup.dispatchTouchEvent…返回true直接处理,false时调用onTouchEvent()。

ViewGroup:分发、拦截和消费
viewGroup的onInterceptTouchEvent返回true表示拦截,自己的onTouchEvent消费,不再下发。返回false传递给子View.dispatchTouchEvent()。

View:只分发和消费
dispatchTouchEvent返回true直接处理,false传递给父View。

[2] 三个核心事件

dispatchTouchEvent() :true事件被当前视图消费掉,false交给父类的onTouchEvent处理。
onInterceptTouchEvent(): true拦截 自己的onTouchEvent()消费,false传递给子View。
onTouchEvent: true消费,false给父类。

[3] 优先级

dispatchTouchEvent–onTouch–onInterceptTouchEvent–onTouchEvent
onTouch是View的触摸监听器Listener中的方法,优先级更高,返回false会调用onTouchEvent(),返回true表示直接处理了onTouchEvent不会被调用。

什么时候会触发Action_cancel事件?
当父View第一次拦截了Action_up/move,子View会收到Action_cancel。
触摸到控件,但不是在它的区域抬起,会收到Action_cancel。

点击事件被父拦截了,但是想往下传咋办?
重写子类的requestDisAllowInterceptTouchEvent()返回true,就不会执行父类的拦截方法onInterceptTouchEvent(),剥夺父View对除了Action_down以外事件的控制权。

解决View的事件冲突:
常见滚动布局和RecyclerView的滑动冲突,或者RecyclerView嵌套滑动冲突。
父需要就拦截,子要处理就剥夺父View的处理权。
一个事件序列只能被一个View拦截消耗。所有事件都会交给它处理。

同时对子View和父View设置点击方法,优先响应?
子View,父要优先需要对事件进行拦截。

---- 初次学习,记录有误欢迎指正

参考资料:
https://blog.csdn.net/guolin_blog/article/details/9097463 郭霖大神 上
https://guolin.blog.csdn.net/article/details/9153747 下

Android-事件分发机制(源码层面)相关推荐

  1. 深入解析Android事件分发机制源码(1)

    有关事件分发的文章,网上已经有了太多太多,但是看了很多,大部分都只是讲解了最外层表现给开发者看的结果,并没有深入讲解,为何会得到这个现象.基于透过现象看本质的思想,趁着手头没有太多活,写下这篇博客,一 ...

  2. android SDK-25事件分发机制--源码正确解析

    android SDK-25事件分发机制–源码正确解析 Android 事件分发分为View和ViewGroup的事件分发,ViewGroup比View过一个拦截判断,viewgroup可以拦截事件, ...

  3. android 事件分发 代码解析,Android事件分发之源码分析

    原文首发于微信公众号:躬行之,欢迎关注交流! 上篇文章中叙述了 Android 事件分发的大致流程,下面从 Activity.ViewGroup.View 三个方面介绍事件的相关方法,小节如下: Ac ...

  4. Android View系列(二):事件分发机制源码解析

    概述 在介绍点击事件规则之前,我们需要知道我们分析的是MotionEvent,即点击事件,所谓的事件分发就是对MotionEvent事件的分发过程,即当一个MotionEvent生成以后,系统需要把这 ...

  5. Android事件分发之源码分析,kotlin库

    //这里又调用了FrameLayout中的dispatchTouchEvent方法 return super.dispatchTouchEvent(event); } - } 由于在 FrameLay ...

  6. Android 事件分发机制

    Android 事件分发机制  demo验证:  https://blog.csdn.net/hty1053240123/article/details/77866302 目录 1.基础认知 2.事件 ...

  7. Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    <div id="container">         <div id="header">     <div class=&qu ...

  8. Android 系统(199)---Android事件分发机制详解

    Android事件分发机制详解 前言 Android事件分发机制是Android开发者必须了解的基础 网上有大量关于Android事件分发机制的文章,但存在一些问题:内容不全.思路不清晰.无源码分析. ...

  9. View的事件体系之三 android事件分发机制详解(下)

    接着上一篇来分析事件分发机制,在看了各位大牛的关于事件分发机制的分析后茅塞顿开,之前看过好几遍郭霖,弘扬以及玉刚大神关于事件体系的讲解,一直看不懂,比较模糊,最近复习时,看到一篇博文,写的相当精彩,看 ...

最新文章

  1. Mask Rcnn训练自己的航拍数据集
  2. 布局AR VR领域?Tessera 8.5亿美元收购音频公司DTS
  3. 渗透Xen hypervisor
  4. 【Spring】23、ApplicationContext ,ApplicationContextAware,Listener,Event 的关系解读
  5. 使用jquery-easyui写的CRUD插件(2)
  6. C# 学习笔记(11)蓝屏小工具
  7. 机器学习笔记 network compression
  8. Swift之深入解析Xcode13对Swift对象生命周期的优化
  9. Apache Apollo REST API
  10. 进入实现类快捷键_实测30个自带快捷键,原来键盘也这么好用!
  11. py thon 多线程(转一篇好文章)
  12. opencv4 c++ 提取图片中的白色区域_OpenCV4.5.0 更快的SIFT,更强的数字识别,以及色彩校正、深度融合...
  13. RDIFramework.NET开发实例━表约束条件权限的使用-WinForm
  14. 音频加速 foobar_如何使用Foobar2000将音频CD翻录到FLAC
  15. 树莓派开发实战项目 智能家居--简单工厂模式(摄像头图片获取)
  16. 开始尝试淘宝直通车推广
  17. 机器人图形变变变_中班公开课数学教案《图形变变变》
  18. 戴尔电脑录屏怎么录?这3个方法,教你轻松录屏
  19. [活动预告] Substrate 中的 IBC 跨链模块技术分享 Substrate-ibc
  20. css -- position : absolute 在不同的浏览器位置不同

热门文章

  1. 信创引领丨呼叫中心加速适配国产化
  2. 食品加工行业MES与APS的应用
  3. 2022-03-26-Subline3的常用快捷键
  4. OSChina 周四乱弹 —— 为什么现在社会越来越鄙视直男
  5. 全球与中国一体化VR摄影机市场现状及未来发展趋势2022-2028
  6. 用java代码执行命令行并获取返回结果
  7. SpringBoot 3.0 来啦!
  8. 头像照片汇聚logo视频片头ae竖屏模板
  9. Allegro基本规则设置指导书之Electrical Total Etch Length
  10. 使用Origin画出复杂网络博弈中合作率时间演化图(学术论文)