前面了解了【view的事件分发】,这里我们继续研究一下view的事件分发和处理,从而找到事件冲突的原因及其解决方案。
一、概念认知
在view的onTouchEvent(方法中)处理MotionEvent()的事件有:
DOWN: 手指触摸屏幕的事件
UP: 手指离开屏幕是的事件
MOVE: 手指在屏幕上移动时的事件
CANCEL: 当事件被拦截的时候就会触发
1)事件类型:
在view的onTouchEvent(方法中)处理MotionEvent()的事件有:
DOWN: 手指触摸屏幕的事件
UP: 手指离开屏幕是的事件
MOVE: 手指在屏幕上移动时的事件
CANCEL: 当事件被拦截的时候就会触发
2)事件分发的类与方法
从源码角度来分析,事件分发的流程:
activity--->dispatchTouchEvent()
PhoneWindow --->superDispatchTouchEvent()
DecorView --->superDispatchTouchEvent()
ViewGroup--->dispatchTouchEvent()
view--->dispatchTouchEvent()
view--->onTouchEvent()
3)事件冲突:
只有一个事件,但处理的对象有多个,而真正处理的对象并不是期待的对象,这样就发生了冲突;
也就是说,一个事件可以由多个view处理时会出现事件冲突(如滚动嵌套),而一个view对一个事件有多个处理方式时也会出现冲突(如touch 和click 事件)。
二、事件分发的流程
在学习事件分发之前,有些状态或标记是通过位运算符来运算的,所以下面先简单了解一下位运算符:
& 比较位的值都是1的时候,该位的结果才是1
| 比较的位只要有一位是1 ,该位的结果就是1
~ 将当前位进行0 1 的交换,即0 换成1 ,1 换成0
2.1)、DOWN 事件的处理与分发流程:
ViewGroup.java中dispatchTouchEvent(MotionEvent ev) 的源码中
2.1.1)重置mGroupFlags
执行的结果 mGroupFlags &= mGroupFlags & (~FLAG_DISALLOW_INTERCEPT);
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//会重置mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2.1.2)事件是否拦截
用上一步重置的mGroupFlags的值替换下面的表达式:
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
替换后的结果是:
final boolean disallowIntercept = ( mGroupFlags & (~FLAG_DISALLOW_INTERCEPT)& FLAG_DISALLOW_INTERCEPT) != 0;
由此可以判断disallowIntercept 为false; 就会调用 onInterceptTouchEvent(ev) 来判断是否被拦截,这个方法可在自定义view中被重写的。
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
2.1.3)事件处理与分发
1) 当被拦截的时候,即 onInterceptTouchEvent(ev)方法返回为true
A) 直接跳过下面的 if (!canceled && !intercepted)条件 ;
由于首次事件处理,那么mFirstTouchTarget==null成立,然后就进入if (mFirstTouchTarget == null) 里面执行,这时候就会进入到dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
注意:第二个参数canceled 是false ,第三个参数是null
if (!canceled && !intercepted) {
//此处忽略
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//此处忽略
}
}
B) 进入dispatchTransformedTouchEvent()方法:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
//此处条件不成立,暂时忽略
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
//此处条件不成立,暂时忽略
}
// Done.
transformedEvent.recycle();
return handled;
}
说明:
由于传入的第三个参数是null ,就会直接进入该方法的最底部,调用super.dispatchTouchEvent(transformedEvent); 而当前viewGroup的super是view;这样就很显