一 事件分发机制过程
当触发一个touch事件时:
- 事件首先被分发到Activity的dispatchTouchEvent() 方法中,会先将事件分发给Window处理
- Window调用super.dispatchTouchEvent()方法,super.dispatchTouchEvent()在PhoneWindow实现类中处理
- 然后具体实现是调用了DecorView的superDispatchTouchEvent()方法
- 最后DecorView调用ViewGroup的dispatchTouchEvent()方法进行事件分发
最终走到了我们在setContentView方法中设置的ViewGroup中,在ViewGroup中分发的时候,首先会调用onInterceptTouchEvent()方法判断是否需要拦截事件,如果需要拦截,mFirstTouchTarger为null,如果不拦截,则对子View进行事件分发处理,最终到dispatchTransformedTouchEvent()方法,根据子View是否为空进行对应的处理,如果子View为空,则进入View的dispatchTouchEvent()方法,最终调用onTouchEvent()消费方法处理,如果不为空,则进入子View dispatchTouchEvent()方法。
需要注意的是,在事件分发中三个重要的方法
1、dispatchTouchEvent():方法返回值为true表示事件被当前视图消费掉; 返回为false表示 停止往子View传递和分发,交给父类的onTouchEvent处理;如果事件分发返回系统默认的 super.dispatchTouchEvent(ev),事件将分发给本层的事件拦截onInterceptTouchEvent 方法进行处理
2、onInterceptTouchEvent() : return false 表示不拦截,需要继续传递给子视图。return true 拦截这个事件并交由自身的onTouchEvent方法进行消费;如果返回super.onInterceptTouchEvent(ev),默认表示拦截该事件,并将事件传递给当前View的onTouchEvent方法,和return true一样。
3、 onTouchEvent() : return false 是不消费事件,会被传递给父视图的onTouchEvent方法进行处理。return true 是消费事件;如果return super.dispatchTouchEvent(ev),则表示不响应事件,结果与return false一样。
二 onTouch 和onTouchEvent 的区别
onTouch:
onTouch方法是View的 OnTouchListener接口中定义的方法, 当一个View绑定了OnTouchLister后,当有touch事件触发时,就会调用onTouch方法
onTouchEvent:
onTouchEvent 处理点击事件在dispatchTouchEvent中调用
onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。 假如onTouch方法返回false,会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。 内置诸如click事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发
三 view的onTouchEvent,OnClickListerner和OnTouchListener的onTouch方法 三者优先级
1.dispatchTouchEvent中限制性mOnTouchListener.onTouch() onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。
2.假如onTouch方法返回false会接着触发onTouchEvent,返回true,onTouchEvent方法不会被调用。
3.onClick事件是在onTouchEvent的MotionEvent.ACTION_UP事件通过performClick() 触发的。 OnTouchListener中onTouch方法如果返回true,则不会执行view的onTouchEvent方法,也就更不会执行view的onClickListener的onClick方法,返回false,则两个都会执行。
四 ACTION_CANCEL什么时候触发
1.如果在父View中拦截ACTION_UP或ACTION_MOVE,在第一次父视图拦截消息的瞬间,父视图指定子视图不接受后续消息了,同时子视图会收到ACTION_CANCEL事件。一般是系统自己处理
2.如果触摸某个控件,但是又不是在这个控件的区域上抬起(移动到别的地方了),就会出现action_cancel。
五 点击事件被拦截,但是想传到下面的View,如何操作
重写子类的requestDisallowInterceptTouchEvent()方法返回true就不会执行父类的onInterceptTouchEvent(), 可将点击事件传到下面的View, 剥夺了父view 对除了ACTION_DOWN以外的事件的处理权。
六 同时对父 View 和子 View 设置点击方法,优先响应哪个
优先响应子 view。
如果先响应父 view,那么子 view 将永远无法响应,父 view 要优先响应事件,必须先调用 onInterceptTouchEvent 对事件进行拦截,那么事件不会再往下传递,直接交给父 view 的 onTouchEvent 处理。
Android系统中ViewGroup的拦截事件默认不拦截
七 总结
-
如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发
-
可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法
-
子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截
-
一个点击事件产生后,它的传递过程如下: Activity->Window->View。顶级View接收到事件之后,就会按相应规则去分发事件。如果一个View的onTouchEvent方法返回false,那么将会交给父容器的onTouchEvent方法进行处理,逐级往上,如果所有的View都不处理该事件,则交由Activity的onTouchEvent进行处理
-
如果某一个View开始处理事件,如果他不消耗ACTION_DOWN事件(也就是onTouchEvent返回false),则同一事件序列比如接下来进行ACTION_MOVE,则不会再交给该View处理
-
ViewGroup默认不拦截任何事件
-
诸如TextView、ImageView这些不作为容器的View,一旦接受到事件,就调用onTouchEvent方法,它们本身没有onInterceptTouchEvent方法。正常情况下,它们都会消耗事件(返回true),除非它们是不可点击的(clickable和longClickable都为false),那么就会交由父容器的onTouchEvent处理
-
点击事件分发过程如下 dispatchTouchEvent—->OnTouchListener的onTouch方法—->onTouchEvent–>OnClickListener的onClick方法。也就是说,我们平时调用的setOnClickListener,优先级是最低的,所以,onTouchEvent或OnTouchListener的onTouch方法如果返回true,则不响应onClick方法
关注公众号:Android老皮
解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版
内容如下:
1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路
敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔