这是学习《android开发艺术探究》的笔记,如果有什么不对的地方,欢迎指出。
1.验证P143第三点
验证 某个ViewGroup一旦决定拦截事件,那么这一个事件序列的其他事件只能由它来处理,并且它的onInterceptTouchEvent不会再被调用(事件直接由onTouchEvent处理)。
情景1. onInterceptTouchEvent始终拦截事件(补充:ViewGroup默认是不拦截事件的)
public class MyViewGroup extends FrameLayout {
private static final String TAG = "MyViewGroup";
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
String ActionStr = Util.getActionStr(action);
boolean consume;
consume = true;
Log.i(TAG, "onInterceptTouchEvent"+(consume?"拦截":"不拦截") + ActionStr);
return consume;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
String ActionStr = Util.getActionStr(action);
Log.i(TAG, "onTouchEvent: " + ActionStr);
return true;
}
}
结果:ACTOIN_DOWN被拦截后,后续事件直接交由onTouchEvent处理,没有经过onInterceptTouchEvent。
onInterceptTouchEvent拦截ACTION_DOWN
onTouchEvent: ACTION_DOWN
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_UP
情景2. onInterceptTouchEvent不拦截ACTION_DOWN,拦截ACTION_MOVE
public class MyViewGroup extends FrameLayout {
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
String ActionStr = Util.getActionStr(action);
boolean consume;
if (action == MotionEvent.ACTION_DOWN) {
consume = false;
} else {
consume = true;
}
Log.i(TAG, "onInterceptTouchEvent"+(consume?"拦截":"不拦截") + ActionStr);
return consume;
}
.....
}
结果:ACTOIN_MOVE被拦截后,后续事件直接交由onTouchEvent处理,没有经过onInterceptTouchEvent。
onInterceptTouchEvent不拦截ACTION_DOWN
onInterceptTouchEvent拦截ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_MOVE
onTouchEvent: ACTION_UP
2.源码分析
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); // 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 关于源码的几点说明
- actionMasked == MotionEvent.ACTION_DOWN的含义
按下动作,即事件序列的起始动作 - mFirstTouchTarget != null的含义
mFirsetTouchTarget表示的是处理事件的子元素
mFirstTouchTarget != null表示有子元素处理事件
mFirstTouchTarget == null表示没有子元素处理事件 - actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null 的含义
事件序列的起始动作或者已有子元素处理事件(注意,一旦ViewGroup拦截了事件,mFirstTouchTarget会被清空)
2.2 源码逻辑
如果是事件序列的起始动作或者已有子元素在处理事件,则调用onInterceptionTouchEvent判断是否拦截事件,否则默认拦截该事件
而如果"某个ViewGroup一旦决定拦截事件"则
后续事件不可能是ACTION_DOWN 即 actionMasked == MotionEvent.ACTION_DOWN不成立
-
mFirstTouchTarget也会被置空,即mFirstTouchTarget!=null不成立(下面这段代码会将mFirstTouchTarget置空)
TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;//拦截时,cancelChild为true if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next;//会将mFirsrtTouchTarget置空 } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } }
所以actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null不成立,onInterceptTouchEvent不会被调用,intercept直接赋值为true,所以书中原文才这样描述
某个ViewGroup一旦决定拦截事件,那么这一个事件序列的其他事件只能由它来处理,并且它的onInterceptTouchEvent不会再被调用