1、什么是事件分发机制

当用户触摸屏幕时,会产生一个touch事件,这个touch事件(motionEvent)传递到某个具体的view处理的整个过程

用户触摸屏幕会产生一个事件流(ACTION_DOWN -> ACTION_MOVE -> ACTION_UP)

一般来说,view负责处理action_down事件后,会由这个view来处理接下来的事件(注意一般来说)

2、核心方法

  • activity: dispatchTouchEvent / onTouchEvent

  • viewGroup: dispatchTouchEvent / onInterceptTouchEvent / onTouchEvent

  • view: dispatchTouchEvent / onTouchEvent

3、Activity的处理

onUserInteraction()是个空方法,只要接收到down事件,就会执行(用户可重写)。

如果getWindow().superDispatchTouchEvent()返回true,则意味着事件被viewGroup或者子view消费掉,则activity.dispatchTouchEvent()也返回true,事件分发结束;若子view或者viewGroup没有对事件进行处理,则getWindow().superDispatchTouchEvent()返回false , 则执行activity.onTouchEvent(), 无论activity.onTouchEvent() 返回true 还是false, 事件分发都结束。

Activity#dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {onUserInteraction(); //空方法,用户可自行实现}if (getWindow().superDispatchTouchEvent(ev)) {return true; //如果被viewGroup/view消费,则事件分发结束}return onTouchEvent(ev); //没有任何view消费,事件也分发结束
}
复制代码

Activity#onTouchEvent

如果没有任何view消费这个事件,则会执行activity#onTouchEvent()

public boolean onTouchEvent(MotionEvent event) {if (mWindow.shouldCloseOnTouch(this, event)) { //点击区域在边界外,则返回truefinish();return true;}return false;
}
复制代码

Window#shouldCloseOnTouch()

点击区域在边界外,则返回true,否则返回false

public boolean shouldCloseOnTouch(Context context, MotionEvent event) {final boolean isOutside =event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)|| event.getAction() == MotionEvent.ACTION_OUTSIDE;if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {return true;}return false;}
复制代码

getWindow()#superDispatchTouchEvent()

以前我们说过getWindow返回的是一个PhoneWindow对象,PhoneWindow对象是在activity.attach()里实例化的。

mDecor是通过installDecor()实例化的,是window的根布局。mDecor继承自FrameLayout,FrameLayout继承自ViewGroup。即motionEvent对象由Activity传递到了ViewGroup。(activity - phoneWindow - mDecor - ViewGroup )

PhoneWindow#superDispatchTouchEvent()

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);
}
复制代码

DecorView#superDispatchTouchEvent(event)

public boolean superDispatchTouchEvent(MotionEvent event) {return super.dispatchTouchEvent(event);}
复制代码

4、ViewGroup的处理

ViewGroup每次事件分发时,会先判断disallowIntercept(默认为false) 和 onInterceptTouchEvent (返回false, 不拦截 ) , 决定ViewGroup是否拦截事件,如果不拦截事件,则遍历ViewGroup的子View,调用 view.dispatchTouchEvent( ), 即完成了由ViewGroup传递到View事件。

若点击的是空白处(没有任何View处理此事件),或者手动重写onIntercepTouchEvent()返回为true (拦截),则调用super.DispatchTouchEvent( ),即View.DispatchTouchEvent(ViewGroup继承自View)。由ViewGroup自己处理onTouch事件,则由 ViewGroup 的 onTouch() -> onTouchEvent() -> performClick() -> onClick()

ViewGroup本身是没有onTouchEvent()方法的,所以实际上是调用了父类View.onTouchEvent()

ViewGroup#dispatchTouchEvent()

disallowIntercept 是否禁用事件拦截的功能(默认是false)

disallowIntercept为true,则不拦截;false则由onInterceptTouchEvent()来决定是否拦截,onInterceptTouchEvent()返回true则拦截,false则不拦截

  • disallowIntercept可在子View中通过getParent().requestDisallowInterceptTouchEvent()设置
  • onInterceptTouchEvent()用户可重写ViewGroup的onInterceptTouchEvent()
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {boolean handled = false; //是否消费事件if (onFilterTouchEventForSecurity(ev)) {// 如果是个down事件,则把之前的事件都抛掉不管。if (actionMasked == MotionEvent.ACTION_DOWN)//判断是否需要拦截final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {if (!disallowIntercept) { //只有diallowIntercept为false,且onInterceptTouchEvent为true时才拦截intercepted = onInterceptTouchEvent(ev); } else {//如果diallowIntercept为true,则不拦截intercepted = false; }} else {// 如果这不是个down事件,也不是上一个的down事件,则意味着拦截(异常情况)intercepted = true;}//判断事件是否已经取消final boolean canceled = resetCancelNextUpFlag(this)|| actionMasked == MotionEvent.ACTION_CANCEL;if (!canceled && !intercepted) { // 如果不取消也不拦截,则由子View进行处理//遍历并分发给子View进行处理if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {// Child wants to receive touch within its bounds.break;}// handled}return handled;}
复制代码

ViewGroup#onInterceptTouchEvent()

在 dispatchTouchEvent()内部,判断是否拦截事件MotionEvent()

public boolean onInterceptTouchEvent(MotionEvent ev) {if (ev.isFromSource(InputDevice.SOURCE_MOUSE)&& ev.getAction() == MotionEvent.ACTION_DOWN&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)&& isOnScrollbarThumb(ev.getX(), ev.getY())) {return true;}return false;
}
复制代码

ViewGroup#dispatchTransformedTouchEvent

如果有child,则执行child.disptachTouchEvent(event)

没有child,则执行super.dispatchTouchEvent (super也就是View)

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {final boolean handled;if (child == null) {handled = super.dispatchTouchEvent(event);} else {handled = child.dispatchTouchEvent(event);}return handled;}
复制代码

5、View的事件处理

onTouch优先于onTouchEvent,更优先于onClick()

View#dispatchTouchEvent()

public boolean dispatchTouchEvent(MotionEvent event) {boolean result = false;ListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {result = true; //返回true 则事件已被View消费掉,不再往下传递}if (!result && onTouchEvent(event)) {result = true;}}return result;
}
复制代码

//mTouchListener.onTouch(this, event) 返回true 则事件已被View消费掉,不再往下传递

button.setOnTouchListener(object : View.OnTouchListener {override fun onTouch(v: View?, event: MotionEvent?): Boolean {return true}
})
复制代码

View#onTouchEvent()

public boolean onTouchEvent(MotionEvent event) {final float x = event.getX();final float y = event.getY();if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {switch (action) {case MotionEvent.ACTION_UP:if (mPerformClick == null) {mPerformClick = new PerformClick();}if (!post(mPerformClick)) {performClick();}break;case MotionEvent.ACTION_DOWN:break;case MotionEvent.ACTION_CANCEL: //事件取消(非人为)break;case MotionEvent.ACTION_MOVE:break;}return true; //若该控件可点击,则一定返回true}return false; //若该控件不可点击,则一定返回false
}
复制代码

View#performClick()

public boolean performClick() {final boolean result;final ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {li.mOnClickListener.onClick(this); //onTouch() 回调优先于 onClick() 回调result = true;} else {result = false;}return result;
}
复制代码

总结

责任链模式,由Activiy -> ViewGroup -> View

参考

Android事件分发机制 详解攻略,您值得拥有

转载于:https://juejin.im/post/5b693b6fe51d4519915514fb

View事件分发机制(源码 API27)相关推荐

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

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

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

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

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

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

  4. Android面试老生常谈的 View 事件分发机制,看这一篇就够了

    本文首发我的微信公众号:徐公,想成为一名优秀的 Android 开发者,需要一份完备的 知识体系,在这里,让我们一起成长,变得更好~. 在 Android 开发当中,View 的事件分发机制是一块很重 ...

  5. View事件分发机制(源码分析篇)

    01.Android中事件分发顺序 1.1 事件分发的对象是谁 事件分发的对象是事件.注意,事件分发是向下传递的,也就是父到子的顺序. 当用户触摸屏幕时(View或ViewGroup派生的控件),将产 ...

  6. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  7. 【Android View事件分发机制】关于拦截事件的注意点

    在父容器拦截事件时,为什么不能拦截DOWN事件呢? 先看看源码: 回顾一下事件分发机制原理,当事件来了之后,如果父容器不拦截,则会询问其child view ,当某child view 有事件需求,父 ...

  8. Android View 事件分发机制详解

    想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...

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

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

最新文章

  1. avpicture_fill的实现
  2. java设计模式---代理模式
  3. 服务器电源维修哪里便宜,服务器电源维修
  4. 泛微oa系统什么框架_泛微移动办公OA系统走进江苏国曜信息科技有限公司
  5. 3.6 Spark安装与体验
  6. Android必知App 常用图标尺寸规范汇总
  7. nvidia jetson xavier打开风扇,并设置开机启动
  8. Sensor 数据整理
  9. Facebook 开源微光效果 Shimmer
  10. 漫画:如何给女朋友解释灭霸的指响并不是真随机消灭半数宇宙人口的?
  11. 骁龙888发布,小米11首发,有14家厂商首批搭载!
  12. 最小二乘法和岭回归区别
  13. 微型计算机的五大硬件组成,计算机系统的组成,计算机硬件的五大部分是什么...
  14. mysql 退出数据库_mysql怎样退出使用数据库
  15. uboot 或者 linux 下限制 sata speed
  16. 局部非饱和性的含义_范里安-微观经济学现代观点讲义(new)
  17. 虚拟环境内使用pip安装torch内存爆掉
  18. 2006-4-23八达岭长城
  19. 手推公式带你轻松理解L1/L2正则化
  20. 【组成原理-总线】总线的概念和计算

热门文章

  1. 3 个重要因素,带你看透 AI 技术架构方案的可行性
  2. 最新成果被AAAI-20收录,腾讯安全科恩实验室加快AI产业化应用
  3. 万字干货|逻辑回归最详尽解释
  4. 人工智能下一个前沿:可解释性
  5. 人工智能基础-向量的基本几何意义
  6. ICLR 2019计算机视觉、NLP、图模型、对抗学习、表示学习和元学习
  7. 英国最新报告:40% AI公司其实没用任何AI技术
  8. python中defaultdict()函数的介绍以及应用场景
  9. 程序实现:由给定几个数确定凸组合系数,组成一个给定的数
  10. 国外发明的10大仿生机械,这才是真正的黑科技!