这是学习《android开发艺术探究》的笔记 如果有什么不对的地方,欢迎指出。
先说明几个词语的含义
- 事件序列:从手指接触屏幕到手指离开屏幕所触发的事件,一般包含以ACTION_DWMO开始,中间包含若干个ACTION_MOVE,以ACTION_UP结束。
- 事件:一般指的是同一事件序列中的某个事件。
- 处理事件:指onTouchEvent接受到事件。
- 消费事件: 指onTouchEVent接受到事件后,返回true。
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;
}
if (!canceled && !intercepted) {
for(遍历child){
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
newTouchTarget = addTouchTarget(child, idBitsToAssign);
break;
}
}
}
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
}else{
// dispatched to it. Cancel touch targets if necessary.
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;
}
}
}
分析
disallowIntercept 不允许拦截事件默认为false。通过viewGroup.requestDisallowInterceptTouchEvent(boolean)进行设置
dispatchTransformedTouchEvent
/*
*
* Transforms a motion event into the coordinate space of a particular child view,
* filters out irrelevant pointer ids, and overrides its action if necessary.
* If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
}
- child==null调用的是当前View的dipathTouchEvent方法,最终会调用当前View的onTouchEvent。
- child!=null调用的是childe的dipathTouchEvent方法,最终会调用child的onTouchEvent。
- addTouchTarget 给mFirstTouchTarget赋值
/**
* Adds a touch target for specified child to the beginning of the list.
* Assumes the target child is not already present.
*/
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
如果有child消费了事件,则会调用 addTouchTarget会给mFirstTouchTarget赋值。
mFirstTouchTarget表示处理事件的child。
如果mFirstTouchTarget!=null,表示有child正在处理事件,
mFirstTouchTarget==null,表示没有child正在处理事件,即ViewGroup正在处理事件
- 在mFirstTouchTarget!=null时,如果事件被拦截,则会将mFirstTouchTarget置为null。如下面这段代码
if(mFirstTouchTarget==null){
}else{
// dispatched to it. Cancel touch targets if necessary.
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不成立,事件默认被拦截,这样事件会直接交由当前ViewGroup处理。
导致actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null不成立的情况。
- ViewGrop拦截了某个事件
- child不消耗ACTION_DOWN事件。