View事件分发简要分析
同事今天问了一个问题 view设置了onClickListener Activity 中设置了 onTouchEvent 为什么 onTouchEvent 不执行,如果不设置就能执行,所以为了回答这个问题,特意看了源码,解决这个问题
###总结流程
boolean consume = false;
public dispatchTouchEvent(MotionEvent event){
if(onInterceptEvent(event){
consume = onTouchEvent(event);
}else{
consume = child.dispatchTouchEvent(event);
}
return consume;
}
###事件分发方向
- 事件的分发方向是从activity中开始的,从activity中的dispatchTouchevent开始
Activity 源码
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev);}
方法中调用了
getWindow().superDispatchTouchEvent(ev)
并且如果返回true activity的dispatchtouchevent 也返回true意味着子view消费了此事件,如果返回false 调用onTouchEvent 方法。
getWindow 当然是调用的 PhoneWindow 的方法,进入此方法
public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event);}
由此可见调用了 decorview 的方法,decorview 是视图view的根,
而decorview 调用的是父类FrameLayout 的dispatchTouchEvent 所以在activity中分析到这里可以得出以下结论
- activity 中的dispatchtouchevent 返回false 才会 调用 ontouchevent ,也就是说没有view消费事件。
- 当给decorview 中的子view 设置了onclicklistener ,没有执行ontouchevent 可以退出 dispatchtouch返回的是true,也就是说子view 消费了相关事件。
View 事件分发
- 事件的传递从activity到decorview 最终到当前view。
- 必然也是首先执行的是view 的dispatchtouchevent
- view 的dispatchTouchevent 代码相对简单,如果说最终activity 要执行 activity的onTouchEvent 也就是view dispathtouchevent 必然返回false ,所以此时与返回true相关的判断条件都不会执行
if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; }}
- 从以上源码可以看出,有两个条件可以得到 返回 true,在第一个条件的
li.mOnTouchListener.onTouch(this, event)
可以看出没有设置onTouchListener 所以这里必然是为false 的所以,只能是第二个条件成立才会 dispatchTouchEvent 返回true, 而这里是 onTouchEvent 返回true 才会得到true;
- 现在把目光注视到 view 的onTouchEvent 中,从源码中可以看出总共有三处代码可能返回true,
第一处
if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);}
第二处
if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; }}
第三处
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE)
从结果上看第三处是我们所需要的,代码也是在这里执行的,当然现在我们锁需要明白的是这三行判断条件。’
- 当调用 view的setOnClickListener 的时候查看源码
public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l;}
从源码中可以看出,主要调用setClickable方法
public void setClickable(boolean clickable) { setFlags(clickable ? CLICKABLE : 0, CLICKABLE);}
setflags 方法
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
通过逻辑运算可以得出 ontouchevent 条件符合所以返回true,从而view dispatchtouchevent 返回true,从而activity 中dispatchtouchevent 返回true ⤧ Next post http基础知识 ⤧ Previous post Handler,Message,MessageQueue,Looper简析