View事件分发机制(源码 API27)
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)相关推荐
- android SDK-25事件分发机制--源码正确解析
android SDK-25事件分发机制–源码正确解析 Android 事件分发分为View和ViewGroup的事件分发,ViewGroup比View过一个拦截判断,viewgroup可以拦截事件, ...
- Android View系列(二):事件分发机制源码解析
概述 在介绍点击事件规则之前,我们需要知道我们分析的是MotionEvent,即点击事件,所谓的事件分发就是对MotionEvent事件的分发过程,即当一个MotionEvent生成以后,系统需要把这 ...
- 深入解析Android事件分发机制源码(1)
有关事件分发的文章,网上已经有了太多太多,但是看了很多,大部分都只是讲解了最外层表现给开发者看的结果,并没有深入讲解,为何会得到这个现象.基于透过现象看本质的思想,趁着手头没有太多活,写下这篇博客,一 ...
- Android面试老生常谈的 View 事件分发机制,看这一篇就够了
本文首发我的微信公众号:徐公,想成为一名优秀的 Android 开发者,需要一份完备的 知识体系,在这里,让我们一起成长,变得更好~. 在 Android 开发当中,View 的事件分发机制是一块很重 ...
- View事件分发机制(源码分析篇)
01.Android中事件分发顺序 1.1 事件分发的对象是谁 事件分发的对象是事件.注意,事件分发是向下传递的,也就是父到子的顺序. 当用户触摸屏幕时(View或ViewGroup派生的控件),将产 ...
- View的Touch事件分发(二.源码分析)
Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...
- 【Android View事件分发机制】关于拦截事件的注意点
在父容器拦截事件时,为什么不能拦截DOWN事件呢? 先看看源码: 回顾一下事件分发机制原理,当事件来了之后,如果父容器不拦截,则会询问其child view ,当某child view 有事件需求,父 ...
- Android View 事件分发机制详解
想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...
- android 事件分发 代码解析,Android事件分发之源码分析
原文首发于微信公众号:躬行之,欢迎关注交流! 上篇文章中叙述了 Android 事件分发的大致流程,下面从 Activity.ViewGroup.View 三个方面介绍事件的相关方法,小节如下: Ac ...
最新文章
- avpicture_fill的实现
- java设计模式---代理模式
- 服务器电源维修哪里便宜,服务器电源维修
- 泛微oa系统什么框架_泛微移动办公OA系统走进江苏国曜信息科技有限公司
- 3.6 Spark安装与体验
- Android必知App 常用图标尺寸规范汇总
- nvidia jetson xavier打开风扇,并设置开机启动
- Sensor 数据整理
- Facebook 开源微光效果 Shimmer
- 漫画:如何给女朋友解释灭霸的指响并不是真随机消灭半数宇宙人口的?
- 骁龙888发布,小米11首发,有14家厂商首批搭载!
- 最小二乘法和岭回归区别
- 微型计算机的五大硬件组成,计算机系统的组成,计算机硬件的五大部分是什么...
- mysql 退出数据库_mysql怎样退出使用数据库
- uboot 或者 linux 下限制 sata speed
- 局部非饱和性的含义_范里安-微观经济学现代观点讲义(new)
- 虚拟环境内使用pip安装torch内存爆掉
- 2006-4-23八达岭长城
- 手推公式带你轻松理解L1/L2正则化
- 【组成原理-总线】总线的概念和计算