物流网站免费源码,网站制作的软件有哪些,淘宝客导购网站建设,js跳转网站转载请标明出处#xff01;http://blog.csdn.net/sahadev_/article/details/23839039 ,当然一般也没人转载。。。 一直想彻底了解View的事件分发过程#xff0c;在网上也看了很多大神的博客#xff0c;但是总有一些东西不是很明白#xff0c;于是自己就根据源码画了一个流程…转载请标明出处http://blog.csdn.net/sahadev_/article/details/23839039 ,当然一般也没人转载。。。 一直想彻底了解View的事件分发过程在网上也看了很多大神的博客但是总有一些东西不是很明白于是自己就根据源码画了一个流程草图思路总算是清晰了。 文章分为View和ViewGroup两部分介绍首先来讲讲View的事件分发
打开View的源码顺着onTouch方法向上找到dispatchPointerEvent(MotionEvent event)的这个方法在View中再没有其它地方调用它估计事件就是从这里传过来的吧。
来贴一下这个方法的源码 public final boolean dispatchPointerEvent(MotionEvent event) {if (event.isTouchEvent()) {return dispatchTouchEvent(event);} else {return dispatchGenericMotionEvent(event);}}看到在这个方法中调用了dispatchTouchEvent的这个方法dispatchTouchEven()暂时没找到它的相关说明权当它返回了true进入dispatchTouchEvent方法: public boolean dispatchTouchEvent(MotionEvent event) {if (mInputEventConsistencyVerifier ! null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);}if (onFilterTouchEventForSecurity(event)) {//noinspection SimplifiableIfStatementListenerInfo 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;}可以看到在这个方法中调用了onTouchEvent(Event)所以可知onTouchEvent(event)是由dispatchTouchEvent触发的。如果有人对onFilterTouchEventForSecurity这方法有疑问API中是这么解释的:True if the event should be dispatched, false if the event should be dropped.这句话就不用翻译了吧相信大家都懂。 如果onTouchEvent返回了true那么dispatchTouchEvent的返回值也为true. 对于dispatchTouchEvent的返回值说明是这么解释的:True if the event was handled by the view, false otherwise. 如果返回true说明这个事件就被当前的这个View消费掉了这里的返回值大家一定要清楚一会在ViewGroup的说明中需要对这块很熟悉。 接下来说一说ViewGroup的事件分发方法
ViewGroup的事件处理相对View相对有些复杂ViewGroup继承于View在View中dispatchTouchEvent是由dispatchPointerEvent触发的所以ViewGroup也是由dispatchPointerEvent触发的这里不重要接着看ViewGroup的dispatchTouchEvent的源代码: Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (mInputEventConsistencyVerifier ! null) {mInputEventConsistencyVerifier.onTouchEvent(ev, 1);}boolean handled false;if (onFilterTouchEventForSecurity(ev)) {final int action ev.getAction();final int actionMasked action MotionEvent.ACTION_MASK;// 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();}// 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;}// Check for cancelation.final boolean canceled resetCancelNextUpFlag(this)|| actionMasked MotionEvent.ACTION_CANCEL;// Update list of touch targets for pointer down, if needed.final boolean split (mGroupFlags FLAG_SPLIT_MOTION_EVENTS) ! 0;TouchTarget newTouchTarget null;boolean alreadyDispatchedToNewTouchTarget false;if (!canceled !intercepted) {if (actionMasked MotionEvent.ACTION_DOWN|| (split actionMasked MotionEvent.ACTION_POINTER_DOWN)|| actionMasked MotionEvent.ACTION_HOVER_MOVE) {final int actionIndex ev.getActionIndex(); // always 0 for downfinal int idBitsToAssign split ? 1 ev.getPointerId(actionIndex): TouchTarget.ALL_POINTER_IDS;// Clean up earlier touch targets for this pointer id in case they// have become out of sync.removePointersFromTouchTargets(idBitsToAssign);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;}}}if (newTouchTarget null mFirstTouchTarget ! null) {// Did not find a child to receive the event.// Assign the pointer to the least recently added target.newTouchTarget mFirstTouchTarget;while (newTouchTarget.next ! null) {newTouchTarget newTouchTarget.next;}newTouchTarget.pointerIdBits | idBitsToAssign;}}}// Dispatch to touch targets.if (mFirstTouchTarget null) {// No touch targets so treat this as an ordinary view.handled dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {// Dispatch to touch targets, excluding the new touch target if we already// 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;if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled true;}if (cancelChild) {if (predecessor null) {mFirstTouchTarget next;} else {predecessor.next next;}target.recycle();target next;continue;}}predecessor target;target next;}}// Update list of touch targets for pointer up or cancel, if needed.if (canceled|| actionMasked MotionEvent.ACTION_UP|| actionMasked MotionEvent.ACTION_HOVER_MOVE) {resetTouchState();} else if (split actionMasked MotionEvent.ACTION_POINTER_UP) {final int actionIndex ev.getActionIndex();final int idBitsToRemove 1 ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}}if (!handled mInputEventConsistencyVerifier ! null) {mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);}return handled;}ViewGroup的dispatchTouchEvent的方法有些长在代码中可以看到ViewGroup将View的该方法彻底重写了没有很明显的看到super.dispatchTouchEvent的字样也就是说还没有着急把事件交给View去处理。 前几行和View都差不错往下看 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;}在第5行中调用了onInterceptTouchEvent(ev);该方法表示当按下事件触发的时候是否要拦截本次事件这个方法的源码是这样的public boolean onInterceptTouchEvent(MotionEvent ev) {return false;}如果不对它进行重写的话它总是返回false也就是说intercepted的值恒为false接着往下看看到 if (!canceled !intercepted) {这一行的时候由于intercepted为false所以事件可以进入if内执行接着往下看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;}}看到这段代码中dispatchTransformedTouchEvent的这个方法开始对ViewGroup的子View进行事件传递了/*** 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) {final boolean handled;// Canceling motions is a special case. We dont 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);if (child null) {handled super.dispatchTouchEvent(event);} else {handled child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}// Calculate the number of pointers to deliver.final int oldPointerIdBits event.getPointerIdBits();final int newPointerIdBits oldPointerIdBits desiredPointerIdBits;// If for some reason we ended up in an inconsistent state where it looks like we// might produce a motion event with no pointers in it, then drop the event.if (newPointerIdBits 0) {return false;}// If the number of pointers is the same and we dont need to perform any fancy// irreversible transformations, then we can reuse the motion event for this// dispatch as long as we are careful to revert any changes we make.// Otherwise we need to make a copy.final MotionEvent transformedEvent;if (newPointerIdBits oldPointerIdBits) {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;}transformedEvent MotionEvent.obtain(event);} else {transformedEvent event.split(newPointerIdBits);}// Perform any necessary transformations and dispatch.if (child null) {handled super.dispatchTouchEvent(transformedEvent);} else {final float offsetX mScrollX - child.mLeft;final float offsetY mScrollY - child.mTop;transformedEvent.offsetLocation(offsetX, offsetY);if (! child.hasIdentityMatrix()) {transformedEvent.transform(child.getInverseMatrix());}handled child.dispatchTouchEvent(transformedEvent);}// Done.transformedEvent.recycle();return handled;}该方法的最终返回值是布尔值意思为是否被处理了请看这里 // Perform any necessary transformations and dispatch.if (child null) {handled super.dispatchTouchEvent(transformedEvent);} else {final float offsetX mScrollX - child.mLeft;final float offsetY mScrollY - child.mTop;transformedEvent.offsetLocation(offsetX, offsetY);if (! child.hasIdentityMatrix()) {transformedEvent.transform(child.getInverseMatrix());}handled child.dispatchTouchEvent(transformedEvent);}这里开始对子View进行事件传递调用子View的dispatchTouchEvent的方法如果被子View消费掉了本次事件dispatchTransformedTouchEvent就返回true再回到那个循环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;}请注意这个方法newTouchTarget addTouchTarget(child, idBitsToAssign);这个方法中的代码是这样的:private TouchTarget addTouchTarget(View child, int pointerIdBits) {TouchTarget target TouchTarget.obtain(child, pointerIdBits);target.next mFirstTouchTarget;mFirstTouchTarget target;return target;}注意将mFirstTouchTarget赋了值不再是null先记住这里待会有用。 好了子View循环完成之后代码会执行到这里 // Dispatch to touch targets.if (mFirstTouchTarget null) {// No touch targets so treat this as an ordinary view.handled dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {// Dispatch to touch targets, excluding the new touch target if we already// 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;if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled true;}if (cancelChild) {if (predecessor null) {mFirstTouchTarget next;} else {predecessor.next next;}target.recycle();target next;continue;}}predecessor target;target next;}}注意到刚刚mFirstTouchTarget被赋了值不再是null不为null就不会执行if中的语句if中的语句都做了些什么呢handled dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);注意第3个参数是Null咱们再返回到dispatchTransformedTouchEvent中(刚才已经贴过方法内的代码): 该方法内有3处super.dispatchTouchEvent(event);因为刚才mFirstTouchTarget的值不为null,所以没有进入if所以ViewGroup没法去调用super.dispatchTouchEvent(event);所以当子View把事件消费完成之后ViewGroup就无法接收到onTouchEvent事件这就是为什么子View返回trueViewGroup就无法处理的原因。 事件传递到这里也就讲完了不知道大家有没有听明白如果有什么疑问可以在下面留言欢迎转载