关于View的事件分发

关于View的事件分发,查看源码:


ViewGroup.java


	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
	    if (mInputEventConsistencyVerifier != null) {
	        mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
	    }
	
	    boolean handled = false;
	    //安全检查,如果窗口没有OBSCURED返回true
	    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;
	        //ACTION_DOWN或者已经找到目标target
	        if (actionMasked == MotionEvent.ACTION_DOWN
	                || mFirstTouchTarget != null) {
	             //是否禁止拦截子view事件,子view可以调用requestDisallowInterceptTouchEvent()设置
	            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;
	        //事件是否传递给目标target
	        boolean alreadyDispatchedToNewTouchTarget = false;
	        if (!canceled && !intercepted) {
	        	//非ACTION_CANCEL并且不拦截,准备向下分发事件
	            if (actionMasked == MotionEvent.ACTION_DOWN
	                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
	                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
	                    //动作为ACTION_DOWN或者ACTION_POINTER_DOWN确定目标target(ACTION_HOVER_MOVE没用过)
	                final int actionIndex = ev.getActionIndex(); // always 0 for down
	                final 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 (childrenCount != 0) {
	                    // Find a child that can receive the event.
	                    // Scan children from front to back.
	                    final View[] children = mChildren;
	                    final float x = ev.getX(actionIndex);
	                    final float y = ev.getY(actionIndex);
	
	                    for (int i = childrenCount - 1; i >= 0; i--) {
	                    	//从后往前循环
	                        final View child = children[i];
	                        
	                        if (!canViewReceivePointerEvents(child)
	                                || !isTransformedTouchPointInView(x, y, child, null)) {							//一般不太可能进入这里(如果子view不可见则canViewReceivePointerEvents返回true;如果even没有在child上,会进入这里
)
	                            continue;
	                        }
	
	                        newTouchTarget = getTouchTarget(child);
	                        if (newTouchTarget != null) {
	                        	//child已经是target,初始情况下也不太可能
	                            // 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);
	                        //将事件分发给view(如果view为null,则自己处理)
	                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
	                            // Child wants to receive touch within its bounds.
	                            mLastTouchDownTime = ev.getDownTime();
	                            mLastTouchDownIndex = i;
	                            mLastTouchDownX = ev.getX();
	                            mLastTouchDownY = ev.getY();
	                            //在addTouchTarget设置mFirstTouchTarget为view
	                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
	                            alreadyDispatchedToNewTouchTarget = true;
	                            //退出for循环
	                            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;
	                }
	            }
	        }//end if (!canceled && !intercepted)
	
	        // Dispatch to touch targets.
	        if (mFirstTouchTarget == null) {
	        	//(1)如果ACTION_DOWN或者ACTION_POINTER_DOWN没有子view处理,自己处理            				//(2)如果(1)成立,那么mFirstTouchTarget为null,也是自己处理
	            // No touch targets so treat this as an ordinary view.
	            handled = dispatchTransformedTouchEvent(ev, canceled, null,
	                    TouchTarget.ALL_POINTER_IDS);
	        } else {
	        	//如果已经找到target,那么分发事件
	            // 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) {		//如果ACTION_DOWN或者ACTION_POINTER_DOWN,并且alreadyDispatchedToNewTouchTarget,表示先前已经处理过了
	                    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;
	}
	
	
	
	 /**
	 * 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 don't 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 don't 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) {
	    	//child为null,自己处理(查看view的dispatchTouchEvent,他调用onTouchEvent)
	        handled = super.dispatchTouchEvent(transformedEvent);
	    } else {
	        final float offsetX = mScrollX - child.mLeft;
	        final float offsetY = mScrollY - child.mTop;
	        //更新事件的x和y坐标
	        transformedEvent.offsetLocation(offsetX, offsetY);
	        if (! child.hasIdentityMatrix()) {
	            transformedEvent.transform(child.getInverseMatrix());
	        }
			//分发
	        handled = child.dispatchTouchEvent(transformedEvent);
	    }
	
	    // Done.
	    transformedEvent.recycle();
	    return handled;
	}
	
	/**
	 * Adds a touch target for specified child to the beginning of the list.
	 * Assumes the target child is not already present.
	 */
	private TouchTarget addTouchTarget(View child, int pointerIdBits) {
	    TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
	    target.next = mFirstTouchTarget;
	    mFirstTouchTarget = target;
	    return target;
	}


 /**
     * Returns true if a child view contains the specified point when transformed
     * into its coordinate space.
     * Child must not be null.
     * @hide
     */
    protected boolean isTransformedTouchPointInView(float x, float y, View child,
            PointF outLocalPoint) {
        float localX = x + mScrollX - child.mLeft;
        float localY = y + mScrollY - child.mTop;
        if (! child.hasIdentityMatrix() && mAttachInfo != null) {
            final float[] localXY = mAttachInfo.mTmpTransformLocation;
            localXY[0] = localX;
            localXY[1] = localY;
            child.getInverseMatrix().mapPoints(localXY);
            localX = localXY[0];
            localY = localXY[1];
        }
        final boolean isInView = child.pointInView(localX, localY);
        if (isInView && outLocalPoint != null) {
            outLocalPoint.set(localX, localY);
        }
        return isInView;
    }




View.java

    /**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    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;
    }


小结:


如果动作为ACTION_DOWN或者非ACTION_DOWN但是事件的目标view已经找到,那么检查disallowIntercept,如果disallowIntercept,不干涉,否则尝试干涉,然后根据onInterceptTouchEvent的结果决定干涉;   
如果是其余动作(不是初始动作,即ACTION_MOVE,ACTION_UP等等)并且事件的目标view没有找到,则会决定干涉。

如果决定不干涉,那么在ACTION_DOWN时候将事件分发下去:调用子view的dispatchTouchEvent,如果view的dispatchTouchEvent返回true,表示它希望处理事件,然后让他处理ACTION_DOWN,并且后续事件也让他处理。


因此,可以得出这样的结论:
    1、如果有多个view都可以处理event,那么按照zorder从上往下寻找目标,如果找到之后,下面的view收不到事件。
    2、如果过ACTION_DOWN就没有找到target,那么后续事件就不会往下分发了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值