Android事件传递机制
最近在做一个图片,文字拖拽缩放的控件,所以研究了下android的事件传递机制,记录在此。
一、Activity——dispatchTouchEvent和onTouchEvent
先来看下Activity里的dispatchTouchEvent源码
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
getWindow().superDispatchTouchEvent(ev),getWindow()返回的是一个PhoneWindow对象,执行了superDispatchTouchEvent(ev)方法,如果返回true表示事件被消费掉。
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
mDecor是一个DecorView对象,继承自FrameLayout,它是一个Activity的root view,通过super.dispatchTouchEvent会把事件分发给各个子view及Activity onCreate方法中setContentView里赋值的view。
二、ViewGroup——dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent
// Check for interception. 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; }
dispatchTouchEvent代码比较多,不全贴了,上边的代码是检查是否拦截事件的,onInterceptTouchEvent(ev)里代码很简单就返回了个false,disallowIntercept用来控制是否禁用拦截事件,可以通过requestDisallowInterceptTouchEvent方法改变值。如果事件没被拦截会执行下面的代码:
final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; final boolean customOrder = isChildrenDrawingOrderEnabled(); for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } }遍历了所有子view,找到点击的view执行dispatchTransformedTouchEvent方法:
if (child == null || child.hasIdentityMatrix()) { if (child == null) { handled = super.dispatchTouchEvent(event); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; event.offsetLocation(offsetX, offsetY); handled = child.dispatchTouchEvent(event); event.offsetLocation(-offsetX, -offsetY); } return handled; }可以看到child.dispatchTouchEvent(event),所以ViewGroup的dispatchTouchEvent依次调用了子view的dispatchTouchEvent方法,当子view的dispatchTouchEvent方法返回true时事件到此结束,返回false时会去执行super.dispatchTouchEvent方法,也就是父类View的dispatchTouchEvent方法,下面介绍View的dispatchTouchEvent方法;
三、View——dispatchTouchEvent、onTouchEvent
先来看dispatchTouchEvent:
public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; }判断了mOnTouchListener是否为空和它的返回值,如果不为空并且返回true,dispatchTouchEvent直接返回true,否则执行onTouchEvent(event),onTouchEvent的默认返回值与CLICKABLE和LONG_CLICKABLE属性有关,例如button默认返回true,而textview默认返回false。如果onTouchEvent返回true及dispatchTouchEvent返回true时间传递结束,否则会执行父布局的onTouchEvent。
第一次写的有点乱
版权声明:本文为博主原创文章,未经博主允许不得转载。