当触摸事件发生时,Activity先接受到事件。
public boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {//在Activity可以实现该方法,处理必要的逻辑//当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法onUserInteraction();}// 若返回trueif (getWindow().superDispatchTouchEvent(ev)) {//1. 获取到的是window的唯一实现类PhoneWindow;//2.该方法由其唯一子类PhoneWindow类实现,// 3.PhoneWindow内部有DecorWindow类,其继承自FrameLayout,是所有界面的父类,//4.FrameLayout继承自ViewGroup类,最终调用到ViewGroup的super.dispatchTouchEvent(event);//实现从Activity到内部View的传递,即内部可优先处理事件,若未true,再由Activity处理。return true;}// 否则:继续往下调用Activity.onTouchEventreturn onTouchEvent(ev);
DecorView内的dispatchToucEvent(可以通过Activity内的onTouchEvent打开DecorView)
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {final Window.Callback cb = mWindow.getCallback();//cb.dispatchTouchEvent(ev) :调用Activity的dispatchTouchEvent()// super.dispatchTouchEvent(ev):交给ViewGroup处理。return cb != null && !mWindow.isDestroyed() && mFeatureId < 0? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
关于Activity的内的onTouchEvent
最后当触摸屏事件未被其下的任何视图处理时调用,这对于处理发生在窗口边界之外的触摸事件非常有用,因为在那里没有接收它的视图。
public boolean onTouchEvent(MotionEvent event) {//处理发生在Window边界外的触摸事件。if (mWindow.shouldCloseOnTouch(this, event)) {finish();return true;}return false;}
// 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等public boolean shouldCloseOnTouch(Context context, MotionEvent event) {if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN&& isOutOfBounds(context, event) && peekDecorView() != null) {//说明事件在边界外,消费事件return true;}
回到getWindow().superDispatchTouchEvent(ev)向内ViewGroup内传递的分析。
ViewGroup内dispatchTouchEvent
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(ev, 1);}//如果事件以可访问且可聚焦的view为目标,则启动正常事件调度。否则交给子view处理点击。if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {ev.setTargetAccessibilityFocus(false);}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) {//当开始一个新的触摸手势时,删除原先的状态。//重置状态,(由于应用程序切换、ANR或其他一些状态改变,底层可能已经取消事件)cancelAndClearTouchTargets(ev);resetTouchState();}//判断是否被打断final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {//判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),//可通过调用requestDisallowInterceptTouchEvent()修改final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {// 对onInterceptTouchEvent()返回值取反// a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),则为trueintercepted = onInterceptTouchEvent(ev);ev.setAction(action);} else {// b. 若在onInterceptTouchEvent()中返回true(即拦截事件),则为false,intercepted = false;}} else {intercepted = true;}//.......此处省略部分代码final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;TouchTarget newTouchTarget = null;boolean alreadyDispatchedToNewTouchTarget = false;if (!canceled && !intercepted) {View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()? findChildWithAccessibilityFocus() : null;if (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {//.......此处省略部分代码final int childrenCount = mChildrenCount;if (newTouchTarget == null && childrenCount != 0) {final float x = ev.getX(actionIndex);final float y = ev.getY(actionIndex);final ArrayList<View> preorderedList = buildTouchDispatchChildList();final boolean customOrder = preorderedList == null&& isChildrenDrawingOrderEnabled();final View[] children = mChildren;//遍历所有的子view,寻找可以传递事件的viewfor (int i = childrenCount - 1; i >= 0; i--) {final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);//如果找到一个可达且可获取焦点的view,让其先获得事件,//如果没有处理,执行异常调度。在在给定的时间范围内,进行双重迭代,这是比较安全的。if (childWithAccessibilityFocus != null) {if (childWithAccessibilityFocus != child) {continue;}childWithAccessibilityFocus = null;i = childrenCount - 1;}if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}//.......此处省略部分代码resetCancelNextUpFlag(child);//将事件转换成特定子view的坐标空间,过滤掉无关的指针ID,并在必要时重写其事件。//如果子view为空,事件将转交给其父类处理。if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//将事件分发给子viewmLastTouchDownTime = ev.getDownTime();if (preorderedList != null) {for (int j = 0; j < childrenCount; j++) {if (children[childIndex] == mChildren[j]) {mLastTouchDownIndex = j;break;}}} else {mLastTouchDownIndex = childIndex;}mLastTouchDownX = ev.getX();mLastTouchDownY = ev.getY();newTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;break;}//此处省略N行........}if (preorderedList != null) preorderedList.clear();}//mFirstTouchTarget:触摸目标链表中的第一次触摸目标。if (newTouchTarget == null && mFirstTouchTarget != null) {//若没有找到可以传递事件的子view,将指针分配到最近最少添加的view。newTouchTarget = mFirstTouchTarget;while (newTouchTarget.next != null) {newTouchTarget = newTouchTarget.next;}newTouchTarget.pointerIdBits |= idBitsToAssign;}}}if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {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;}//此处省略N行........}predecessor = target;target = next;}}// Update list of touch targets for pointer up or cancel, if needed.//此处省略N行........return handled;}
前面判断完分发条件后,对可以获取的view分发事件,主要是通过以下方法:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {final boolean handled;final int oldAction = event.getAction();if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {event.setAction(MotionEvent.ACTION_CANCEL);if (child == null) {//若子view为null,执行其父view的dispatchTouchEventhandled = super.dispatchTouchEvent(event);} else {//若不为null,则传递事件。handled = child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}// 计算要传递的指针的数量。final int oldPointerIdBits = event.getPointerIdBits();final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;//可能会产生一个没有指针数的事件,那么放弃该事件。if (newPointerIdBits == 0) {return false;}//如果指针的数目是相同的,并且不需要执行任何不可逆的转换,//则恢复之前所做的更改,以重用这个调度的事件。//否则需要复印一份。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);}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;}
View内的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {// 如果事件可被可达到且的焦点view处理if (event.isTargetAccessibilityFocus()) {if (!isAccessibilityFocusedViewOrHost()) {return false;}// view有焦点,且获得到事件处理event.setTargetAccessibilityFocus(false);}boolean result = false;if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);}final int actionMasked = event.getActionMasked();if (actionMasked == MotionEvent.ACTION_DOWN) {stopNestedScroll();}if (onFilterTouchEventForSecurity(event)) {//条件1:(mViewFlags & ENABLED_MASK) == ENABLED//a. 该条件是判断当前点击的view是否enable// b. 由于很多View默认enable,故该条件恒定为true//条件2:handleScrollBarDragging(event),事件是否是拖动滚动条if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {result = true;}// 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()// 1. mOnTouchListener != null: 是否注册触摸监听// 2. (mViewFlags & ENABLED_MASK) == ENABLED: 当前被点击的view是否enable// 3. mOnTouchListener.onTouch(this, event) :监听的boolean类型回调值是否是trueListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {//符合条件,未被打断,则事件分发结束result = true;}//若为fasle,调用view的onTouchEvent()方法处理事件。if (!result && onTouchEvent(event)) {result = true;}}//此处省略N行代码..........return result;}
再看onTouchEvent方法()
public boolean onTouchEvent(MotionEvent event) {final float x = event.getX();final float y = event.getY();final int viewFlags = mViewFlags;final int action = event.getAction();if ((viewFlags & ENABLED_MASK) == DISABLED) {//如当前view的状态是disable的if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {setPressed(false);}return (((viewFlags & CLICKABLE) == CLICKABLE|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);}if (mTouchDelegate != null) {//如果TouchDelegate对象不为空,将事件传递给它处理,不再往下。if (mTouchDelegate.onTouchEvent(event)) {return true;}}if (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {switch (action) {case MotionEvent.ACTION_UP://手指抬起actionboolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {//如果view没有获取焦点,为其请求焦点boolean focusTaken = false;if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {focusTaken = requestFocus();}if (prepressed) {setPressed(true, x, y);}if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {removeLongPressCallback();if (!focusTaken) {if (mPerformClick == null) {mPerformClick = new PerformClick();}//执行点击事件if (!post(mPerformClick)) {performClick();}}}//此处省略N行代码......break;case MotionEvent.ACTION_DOWN:mHasPerformedLongPress = false;if (performButtonActionOnTouchDown(event)) {break;}//此处省略N行........if (isInScrollingContainer) {//此处省略N行........postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());} else {setPressed(true, x, y);//判断是不是长按checkForLongClick(0, x, y);}break;case MotionEvent.ACTION_CANCEL://此处省略N行........break;case MotionEvent.ACTION_MOVE://此处省略N行........break;}return true;}return false;}
public boolean performClick() {final boolean result;final ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);li.mOnClickListener.onClick(this);result = true;} else {result = false;}sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);return result;}
小结
Activity获取到dispacth事件,率先处理,再交给其内部的view。
