Android触摸事件分发机制完全解析《一》
Android触摸事件分发机制完全解析
于亚豪的博客地址
最近在做高德地图的时候,由于用户的要求,不得不用ScrollVew嵌套MapView,虽然很官方要求不建议这样做,但也迫于无奈... 魔高一尺,道高一丈.有什么事情是程序员不能解决的,如果有那就是解决两次.
鉴于用到了触摸事件,于是就来总结了Android的触摸事件机制.
首先当用户进行屏幕操作的时候,则有两种情况
一是按键事件
二是触摸事件
按键事件分为长按和点击事件,过于简单,这里不再进行总结.
触摸事件
触摸事件的组成:
一个actionDown
n个actionMove
一个actionUp
一个onClick
一个onLongClick
一个onScroll
Android组件
继承 ViewGroupo比如LinearLayout,ScrollView,GridView,extends-->ViewGroup的View
ViewGroup容器
继承于 View不包含其他的View ,如TextView,Edittext,Butto等
下面介绍一个讲的好的一个博客地址:
MotionEvent
所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。
Android 事件的处理的分类
分发(很多人也称作为传递事件) : dispatchTouchEvent函数
消费: onTouchEvent函数和OnTouchListener函数
拦截:onInterceptTouchEvent函数
我们都知道Android的触摸事件都是从外层传递到内层:由最外层的Activity——>ViewGroup——> ViewGroupo——>.......View.
第一触摸事件传递的开始一定是Activity;
第二传递方式是通过隧道方式传递;
第三一直传递到一个最外层的View,也就是顶级View,由该View的这个方法来进行分发。
对于Activity来说:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isFlag = super.dispatchTouchEvent(ev);
return isFlag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean isFlag = super.onTouchEvent(event);
return isFlag;
}
对于ViewGroup来说:
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean isFlag =super.onTouchEvent(event);
return isFlag;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean isFlag =super.onInterceptTouchEvent(event);
return isFlag;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
boolean isFlag =super.dispatchTouchEvent(event);
return isFlag;
}
对于View来说和Activity一样,只有消费和拦截
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean isFlag = super.dispatchTouchEvent(ev);
return isFlag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean isFlag = super.onTouchEvent(event);
return isFlag;
}
android中的Touch事件都是从ACTION_DOWN开始的:
单手指操作:ACTION_DOWN---ACTION_MOVE----ACTION_UP
多手指操作:ACTION_DOWN---ACTION_POINTER_DOWN---ACTION_MOVE--ACTION_POINTER_UP---ACTION_UP.
如果都不进行拦截,都不消费的基本流程图:
下面我们就用案例进行分析:
activity----》ViewGroup(MyLinearLayout)---》ViewGroup(MySubView)
MyLinearLayout的dispatchTouchEvent返回false。
打印结果:
过程及结果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的dispatchTouchEvent;
而该TouchEventFather 控件的 dispatchTouchEvent 返回 false,表示对获取到的事件停止向下传递,同时也不对该事件进行消费;
由于 TouchEventFather 获取的事件直接来自 TouchEventActivity ,则会将事件返回给 TouchEventActivity 的 onTouchEvent 进行消费;
最后直接由 TouchEventActivity 来响应手指移动和抬起事件。
MyLinearLayout的dispatchTouchEvent返回true。
打印结果:
过程及结果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;
而该TouchEventFather 控件的 dispatchTouchEvent 返回 true,表示分发事件到 TouchEventFather 控件并由该控件的 dispatchTouchEvent 进行消费;
又因为TouchEventActivity 不断的分发事件到 TouchEventFather 控件的 dispatchTouchEvent,而 TouchEventFather 控件的 dispatchTouchEvent 也不断的将获取到的事件进行消费。
MyLinearLayout的onInterceptTouchEvent返回true。
打印结果:
过程及结果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;
而该TouchEventFather 控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventFather 控件的 onInterceptTouchEvent 方法;
而该方法返回 true 表示对所获取到的事件进行拦截并将事件传递给 TouchEventFather 控件的 onTouchEvent 进行处理,TouchEventFather 控件的 onTouchEvent 返回 super.onTouchEvent(ev) 表示对事件没有做任何处理直接将事件返回给上级控件;
由于 TouchEventFather 获取的事件直接来自 TouchEventActivity,所以 TouchEventFather 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventActivity 的 onTouchEvent 进行消费;
后续的事件则会跳过 TouchEventFather 直接由 TouchEventActivity 的 onTouchEvent 消费来自 TouchEventActivity 自身分发的事件。
MyLinearLayout的onInterceptTouchEvent返回false。
打印结果:
过程及结果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;
而该控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventFather 控件的 onInterceptTouchEvent 方法;
该方法返回 false 表示事件会被放行并传递到子控件 TouchEventChilds 的 dispatchTouchEvent 方法;
同样 TouchEventChilds 的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),表示对事件进行分发并向下传递给 TouchEventChilds 控件的 onInterceptTouchEvent 方法;
而TouchEventChilds 的 onInterceptTouchEvent 方法返回 super.onInterceptTouchEvent(ev) ,默认会将事件传递给 TouchEventChilds 的 onTouchEvent 进行处理;
而TouchEventChilds 的 onTouchEvent 返回 super.onTouchEvent(ev) 表示对事件没有做任何处理直接将事件返回给上级控件;
由于 TouchEventChilds 获取的事件直接来自 TouchEventFather,所以 TouchEventChilds 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventFather 的 onTouchEvent 进行消费;
而 TouchEventFather 的 onTouchEvent 也返回了 super.onTouchEvent(ev),同样 TouchEventFather 的 onTouchEvent 也会将事件返回给上级控件;
而 TouchEventFather 获取的事件直接来自 TouchEventActivity,所以 TouchEventFather 控件的 onTouchEvent 会将事件以冒泡方式直接返回给 TouchEventActivity 的 onTouchEvent 进行消费;
后续的事件则会跳过 TouchEventFather 和 TouchEventChilds 直接由 TouchEventActivity 的 onTouchEvent 消费来自 TouchEventActivity 自身分发的事件。
MyLinearLayout的onInterceptTouchEvent返回false。MySubView返回true
打印结果:
过程及结果分析:
事件首先由 TouchEventActivity 的 dispatchTouchEvent 方法分发给 TouchEventFather 控件的 dispatchTouchEvent;
该控件的 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev),事件会分发到 TouchEventFather 的 onInterceptTouchEvent,此方法返回 false 表示放行当先事件;
事件会被传递到子控件 TouchEventChilds 的 dispatchTouchEvent 方法,dispatchTouchEvent 返回 true 表示事件被分发到 TouchEventChilds ,并由 dispatchTouchEvent 方法消费;
后续的事件也会不断的重复上面的逻辑最终被 TouchEventChilds 的 dispatchTouchEvent 消费。
其实如果看源码的话比较复杂,总结一下,基本的规则是:
down事件首先会传递到onInterceptTouchEvent()方法
如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,内部view将也会获取到down,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后一样传递给最终的目标view的onTouchEvent()处理。
如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,内部view不会货到down,后续的move, up等事件也不再传递给onInterceptTouchEvent(),当然,move,up也不会传给view
如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
group的touch和这个机制没关系,与上一级有关系。
来自androidstarjack博客地址:
Hello,伙伴们
长按二维码就可以关注我们啦
(欢迎关注学习和交流)