您的位置:首页 > 其它

ViewGroup的事件拦截、事件分发、事件处理

2017-07-11 14:30 471 查看
上篇博客说了下View的事件分发和事件处理,接着这里说下ViewGroup,ViewGroup多了一个事件拦截,涉及到有三个相应的方法;

dispatchTouchEvent   事件分发
onInterceptTouchEvent   事件拦截
onTouchEvent   事件处理


先看下下面几种不同情况运行的结果;

正常情况:



ACTION_DOWN:

ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent

ACTION_UP:

ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent—>View.onClick

注释掉View.onClick



ACTION_DOWN:

ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent—>ViewGroup.onTouchEvent

将View中的onTouchEvent返回值改成true



ACTION_DOWN:

ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent

ACTION_UP:

ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent

将ViewGroup中的onInterceptTouchEvent返回值改成true



ACTION_DOWN:

ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>ViewGroup.onTouchEvent

结合上面的运行结果,点进ViewGroup源码去看看并分析下出现上面结果的原因;

点击源码找到dispatchTouchEvent()方法,

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
...
if (actionMasked == MotionEvent.ACTION_DOWN) {
//清楚掉一些标记,主要是mFirstTouchTarget
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//intercepted用来标识事件拦截  默认是false
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//调用事件拦截方法,并返回值,
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...

final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;

final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//intercepted为true也就是事件拦截那里返回为true就不会走这个if里面
if (!canceled && !intercepted) {
...
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
if (newTouchTarget == null && childrenCount != 0) {
...
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}

...
}
if (preorderedList != null) preorderedList.clear();
}
...
}
}

if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
...
}
...
return handled;
}


根据拿到的action来进行判断,为MotionEvent.ACTION_DOWN的时候首先调用cancelAndClearTouchTargets(ev);清除掉target

// Handle an initial down.
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();
}


/**
* Clears all touch targets.
*/
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}


会走到clearTouchTargets()方法中将mFirstTouchTarget至为null;mFirstTouchTarget设置为null后,就定义了一个事件拦截的标识,模式是false,

// Check for interception.
final boolean intercepted;


如果事件没有被拦截掉的话,就是说intercepted为false的时候,就会调用这里,

if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}


private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;

// Canceling motions is a special case.  We don't need to perform any transformations
// or filtering.  The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
//如果child为null直接调用自己中的dispatchTouchEvent,否则调用子child中的dispatchTouchEvent
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
...
return handled;
}


走到这里的话,已经调用了ViewGroup.dispatchTouchEvent、ViewGroup.onInterceptTouchEvent、View.dispatchTouchEvent,到了View.dispatchTouchEvent,接着就会走View.onTouch、View.onTouchEvent、View.onClick这个在View的事件处理里面已经说过了,这个时候运行的就是第一个结果;

如果将View的点击事件屏蔽掉,performClick()方法返回的就是false,就是说没有消费事件,

将View中onTouchEvent的返回值改成true,在View的事件中已经详细说了,

将ViewGroup中onInterceptTouchEvent的返回值改成true,下面这个判断就不会走,

if (!canceled && !intercepted){
...
}


没有走,就不会调用dispatchTransformedTouchEvent方法,就不会去走View中的那些方法了;通过ViewGroup和View的源码知道:

如果说子View没有一个地方返回true,只会进来一次响应DOWN事件,代表不需要消费该事件,如果想响应MOVE,UP必须找个地方返回true

对于ViewGroup来说,如果想拦截子View的touch事件,重写onInterceptTouchEvent返回true即可

如果onInterceptTouchEvent返回的是true会执行ViewGroup的onTouchEvent方法,如果子view没有消费事件也会执行onTouchEvent方法

上面如有对ViewGroup的事件处理写的不对的地方,欢迎交流
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息