Android 事件分发机制
1.事件分发机制
事件分发简单而言就是点击事件怎么从Activity流转到ViewGroup,然后流转到View,最后这个点击事件有谁处理,怎么处理?时间分发有三个重要的方法,dispatchTouchEvent(分发事件的方法),onInterceptTouchEvent(拦截事件的方法),onTouchEvent(处理事件的方法)搞清楚Activity,ViewGroup和View的这三个方法,事件分发的机制也就了解了,咱们一个类一个类的进行讲解,首先深呼吸放松一下,然后进入代码的世界,大家慢慢的看。
2.事件分发u型图(网络流行的一个图,能详细反应这个事件分发的流程,我这里直接搬过来了)
3.Activity层的处理
//很简单捕捉点击向下按下的事件,然后执行个空方法,然后判断Window是不是对事件进行了处理有处理Activity就不管了,没有处理得对事件进行处理
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
3.ViewGroup层的处理
①dispatchTouchEvent方法
//处理的结果
boolean handled = false;
//伪代码实现 mFirstTouchTarget 点击事件的处理绑定的View 子布局
//只有是第一次触摸的ACTION_DOWN 按下的瞬间或者 有子的布局处理这个事件的时候 会对事件判断是否拦截
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//FLAG_DISALLOW_INTERCEPT 通过这个标志判断是否对他进行拦截
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
//只要进入到ViewGroup的 dispatchTouchEvent 这个方法证明上次事件处理是在ViewGroup里面执行的OnTouchEvent返回的true
//所有默认是拦截的,因为如果dispatchTouchEvent 返回的是false,根本不会到ViewGroup的dispatchTouchEvent方法
intercepted = true;
}
//当当前不拦截的时候回循环遍历每个子View 判断他们对事件是不是进行了处理,在不在这个区域
if(!intercepted){
if (action == MotionEvent.ACTION_DOWN) {
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
//判断点击的区域是不是在View 的范围内
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
//在点击区域内调用 实际调用的是 子View的dispatchTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//判断是不是子View已经处理过这个事件
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
}
//伪代码
if (mFirstTouchTarget == null) {
// 真实的代码 最后真实调用的onTouchEvent(ev);
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//子View处理
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
//在之前是不是已经让子View 处理过了
handled = true;
} else {
//子View 处理一下
handled=mFirstTouchTarget.child.dispatchTouchEvent(ev);
}
}
总结:当点一次点击event是down事件的时候判断ViewGroup是不是对事件进行拦截,如果进行的话进入ViewGroup的OnTouchEvent();如果不进行拦截进入ViewGroup的dispatch方法里面,当event事件为move的时候首先会判断有没有子View 处理这个时间,有的话,还得判断是不是拦截事件,然后依次执行,如果没有子view拦截的时候直接进入自己的OnTouchEvent(前提是会进入ViewGroup的Dispatch方法,进入前提是ViewGroup或者子View会对事件进行处理,如果都没有处理,在ViewGroup的父布局直接就会返回)
②onInterceptTouchEvent拦截方法
是否对事件进行拦截,拦截的话ViewGroup的onTouchEvent对事件进行处理,不拦截的话会调用子view的dispatchTouchEvent
3.View层的处理
①dispatchTouchEvent方法
boolean result;
//首先用ToucheLister判断 对事件处理与否,如果不处理才会调用自身的onTouchEvent
if(mOnTouchListener !=null && ENABLE
&& mOnTouchListener.onTouch(this, event)){
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
return result;
②onTouchEvent方法
//是否可以点击CLICKABLE(是否可以点) LONG_CLICKABLE(是否可以长按)
// CONTEXT_CLICKABLE每个子控件的这几个默认的属性不一样
//具体的得具体分析
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//控件的状态是Disable的时候不会执行下面的OnClick事件
if ((viewFlags & ENABLED_MASK) == DISABLED) {
return clickable;
}
//如果是可以点击的状态 直接返回true 对这个事件进行消耗
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
//真实调用的是onClick事件
performClickInternal();
break;
}
return true;
}
3.总结
上面我对整个事件分发机制整体进行了概括,有的细节比较简略,如果有什么遗漏的重点内容,大家可以在下面进行评论,谢谢大家!
本文地址:https://blog.csdn.net/u010939317/article/details/107430746
上一篇: vue实现登陆失效重定向
下一篇: Android APP内存优化