参考博客:即于下面博客演变出自己的理解
Android事件分发机制_open-Xu的博客-CSDN博客
郭霖
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)_guolin的博客-CSDN博客
Android事件分发机制完全解析,带你从源码的角度彻底理解(下)_guolin的博客-CSDN博客_android 事件分发机制 郭霖
事件分发机制:整体上遵循递归原则,递是指事件分发传递从Activity---->DecorView ----->ViewGroup---->View
归是指:事件的消费从 View---->ViewGroup---->DecorView---->Activity
要素,连接关系,运行环境如下图
分析 :
1: 方法要素:dispatchTouchEvent是在View中定义的,ViewGroup重写了它。并且View 中的dispatchTouchEvent函数是交给
OnTouchListener处理的,并调用onTouchEvent ----->这是都是View类型控件自己处理
ViewGroup类控件,先是判断是否拦截或者是否禁止拦截这两个变量,来决定是否交给子控件处理,然后才自己处理,自己处理的逻辑和View处理的 逻辑是一样的
2: 要素说明 :
onInterceptTouchEvent(MotionEvent ev) ----》如果返回true,那么子类就没有机会获的事件
onTouchListener.onTouch ----->监听touch事件,onTouch返回值决定是否消费此事件,是否回调onTouchEvent
onTouchEvent ------>控件解析处理事件
setOnClickListener ----->View中的函数,设置点击事件回调onClick函数
3: 关键函数,参数解析
disallowIntercept / requestDisallowIntercept 变量,是否请求父控件禁止拦截事件,默认是false ---->即 默认是拦截的
interceptTouchEvent() -----> 1: 一般在滑动冲突的时候,我们复写这个函数,然后在函数里面加上一个判断条件,什么条件下返回true 拦截事件,什么情况下返回false 不拦截事件。2:系统对于这个函数的处理事:判断 用户的触摸事件是在父控件下的子控件范围内,如果在就返回false 否则返回true ,那要是不在子控件范围内(用户就是点击 activity空白处)或者子控件没有消费事件(dispatchTouchEvent() 返回false )那么interceptTouchEvent()还是返回false, 继续执行super.dispatchTouchEvent 但是这个已经是ViewGroup自己的事件执行了
4: View对 dispatchTouchEvent函数的调用 处理关系:如果返回true (不管是子类返回还是回调父控件返回),事件都是在这一层结束
1: 判断控件enable是否可用 && 是否设置onTouchListener监听 调用onTouch函数
2: onTouch函数:如果返回 false ,说明没有被消费,继续调用 onTouchEvent, 返回true 直接给dispatchTouchEvent返回true
事件在View这一层消费
5:View对onTouchEvent函数分析
该方法会处理一系列的 action_down action_move action_up 事件(按下,滑动,抬起),并在 up事件的时候会回调 performClick() 函数,并且回调OnClickListener.onClick()方法
伪代码分析:
1: ViewGroup 的 dispatchTouchEvent函数
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}
2: View的 dispatchTouchEvent函数
总结
1: android触摸事件是先从 ViewGroup 在传递给View
2: ViewGroup 的dispatchTouchEvent 函数 会 根据 disallowIntercept 来决定是否分发给子控件,此变量的作用就是在 interceptTouchEvent 返回true 之后(不允许事件向子类传递)子类还是想收到 事件这个问题。
3: 子类如果消费事件了,就会通过 dispatchTouchEvent函数传递消费事件的返回值true
4: View 的dispatchTouchEvent 会判断 控件是否设置 enable && onTouchListener 来决定是否走onTouch函数,onTouch函数的返回false, 又会走 onTouchEvent 函数。最终将事件的消费情况传递给 父类
应用:
说了这么都,实际的应用了 ?
1: 一般如果自定义控件继承ViewGroup ,那么需要重写 onInterceptTouchEvent() 函数,并根据自己的的业务逻辑灵活的返回true 或者false -------》比如在ViewPager 中嵌套 ListView 或者RecyclerView 就要控制好 ViewPager 的 onInterceptTouchEvent()函数是否拦截事件了 ,一般都是定义一个 规则,超过某段距离认为用户想横向滑动,这个时候就拦截事件不向RecyclerView 分发,没超过一定距离就 认为用户想垂直滑动,这个时候事件件不拦截事件需要向RecyclerView 分发