图解Android的事件分发
要从源码理解整个事件传递过程,必须先从事件传递的返回值认清整个事件的消费过程,对整个事件有大致的了解,为何事件被消费,为何由子类消费
需要注意的几个点
主要这三个方法
dispatchTouchEvent() —— true消费,false不消费(即为分发);false表示事件停止往下面的视图层级进行传递,同时开始往上面的视图层级的onTouchEvent传递,也称为回溯。
oninterceptTouchEvent() —— true消费,false不消费(即为不拦截)
onTouchEvent() —— true消费,false不消费()
而事件分发一般会经过视图的三个层级:Activity、ViewGroup、View。下表会对视图不同的三个层级拥有的事件分发的相关方法进行整理:
事件分发相关方法 | 方法功能 | Activity | ViewGroup | View |
---|---|---|---|---|
public boolean dispatchTouchEvent | 事件分发 | + | + | + |
public boolean onInterceptTouchEvent | 事件拦截 | + | ||
public boolean onTouchEvent | 事件消费 | + | + | + |
-
view因为没有孩子,没有onInterceptTouchEvent
-
onTouchEvent中对点击事件的具体处理流程大概如下,只要View的CLICKABLE和LONG_CLICKABLE有一个为true,那么它就会消耗事件,返回true。总的来说,View的可不可用不影响是否消耗事件,只要clickable或者longClickable有一个为true,那么它就会消耗事件。
-
ViewGroup默认不拦截任何事件,因为从源码中可以看到ViewGroup的onInterceptTouchEvent方法默认返回false.
-
由View中dispatchTouchEvent可知,如果一个控件是可点击的,那么点击该控件时,dispatchTouchEvent的返回值必定是true
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
- ViewGroup中dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
return true;
}
}
}
}
}
}
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
mMotionTarget = null;
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
return target.dispatchTouchEvent(ev);
}
-
子view的onTouchEvent如果默认false不消费的话,会交给上级的onTouchEvent
-
viewGroup如果onInterceptTouchEvent不拦截,继续交给子view
-
viewGroup如果dispatchTouchEvent返回true,消费掉会还给上级的onTouchEvent
如果上面的点你都清楚的话,说明对事件分发是有清楚认识的,那么接下来上图
android事件分发图解.png 事件链