从大致上来说,触摸事件是系统服务将触摸的状态封装成android.view.MotionEvent
,调用android.app.Activity#dispatchTouchEvent
方法传递到Activity中。
//android.app.Activity#dispatchTouchEvent public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { //空方法,注释说明可以用来说明当前activity与用户开始进行交互以及更好的处理状态栏的通知事件。这里不做深入。 onUserInteraction(); } //这里判断调用的PhoneWindow#superDispatchTouchEvent方法,是最核心的方法。superDispatchTouchEvent方法传入了MotionEvent,将触摸事件传递到activity的布局中,当布局中的控件返回true,表示相关触摸事件被消费掉,直接返回true并结束dispatchTouchEvent。当布局中的控件都返回false,表明布局中的控件没有处理触摸事件,交由当前android.app.Activity#onTouchEvent方法进行处理。 if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); } 复制代码
//PhoneWindow#superDispatchTouchEvent // This is the top-level view of the window, containing the window decor. //mDecor是应用window中的顶级view private DecorView mDecor; //这里直接调用的DecorView#superDispatchTouchEvent @Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); } 复制代码
//DecorView#superDispatchTouchEvent //DecorView调用的父类ViewGroup#dispatchTouchEvent @Override public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); } 复制代码
通过上面三步,将触摸事件从Acivity传递到Activity的contentView中了。
//ViewGroup#dispatchTouchEvent @Override public boolean dispatchTouchEvent(MotionEvent ev) { //对输入事件的完整性做一个检查,检查事件的ACTION_DOWN 和 ACTION_UP 是否一一配对。还会打印错误日志。 if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click. // 无障碍服务相关处理。 if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { ev.setTargetAccessibilityFocus(false); } boolean handled = false; //事件安全性过滤,如果window被遮挡,丢弃此触摸事件。 if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. // 处理点击事件开始的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. // 清除子view的触摸状态, cancelAndClearTouchTargets(ev); // 重置当前的触摸状态。 resetTouchState(); } // Check for interception. // 检查子view的拦截。 final boolean intercepted; // 触摸事件第一次到达这里时mFirstTouchTarget为null。 if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { // public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) // 被调用用来设置是否禁止拦截,默认disallowIntercept为fale final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { // onInterceptTouchEvent用于我们viewGroup中拦截触摸事件,我们重写此方法,根据我们的需要拦截相关触摸事件。当他返回true时,后面一系列触摸事件都会传到此viewGroup的onTouchEvent中而不会再次传到onInterceptTouchEvent中,我们应该在onTouchEvent中返回true以表明触摸事件处理完成。 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; } // If intercepted, start normal event dispatch. Also if there is already // a view that is handling the gesture, do normal event dispatch. //无障碍服务相关处理。 if (intercepted || mFirstTouchTarget != null) { ev.setTargetAccessibilityFocus(false); } // 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; //重置newTouchTaget TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; //判断是否CANCEL事件以及是否被拦截。 if (!canceled && !intercepted) { //无障碍服务相关处理。 View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; // MotionEvent.ACTION_HOVER_MOVE 指针相关的。 // MotionEvent.ACTION_POINTER_DOWN 多指操作的除了第一个手指之外的DOWN // 处理Down事件 if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { //多指触摸的index 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. // 清除其他手指的touchTarget,将touchTarget设置为当前手指 // TODO touchTarget是个链表,在多指触摸的时候,依照手指的顺序排序。 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 ArrayList<View> preorderedList = buildTouchDispatchChildList(); // 是否有自定义绘制顺序 final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); //...省略无障碍相关代码 // 如果mFirstTouchTarget为null,返回null // 如果mFirstTouchTarget的队列中含有此子view,返回此子view的touchTarget。 // 否则返回null。 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; } // 重置cancel和up的标志?TODO resetCancelNextUpFlag(child); // event.getPointerIdBits(); 是否多指触摸手指的判断 TODO // 调用子view的dispatchTouchEvent(transformedEvent);将触摸事件传到子view中。但当子view为null时,调用父view的dispatchTouchEvent(transformedEvent)。 if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); if (preorderedList != null) { // childIndex points into presorted list, find original index for (int j = 0; j < childrenCount; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //mFirstTouchTarget赋值 TODO newTouchTarget = addTouchTarget(child, idBitsToAssign); // 是否已经将触摸事件交给newTouchTarget处理。 alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. // 没有子view接收这个触摸事件,将newTouchTarget赋值为最近一次触摸事件的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. // 没有子view,将自己当作是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; // 将触摸事件分发给touchTarget,不包括已经分发了的newTouchTarget。 // 如果需要给其他touchTarget分发取消事件。 // 这里主要处理除了DOWN以外的事件分发,每次进来这个方法newTouchTarget都会被重置为NUll, // 触摸事件第一次进来传递DOWN事件,如果有子view处理了,newTouchTarget和mFirstTouchTarget会被赋值, // 后续move事件进来mFirstTouchTarget不为空,而newTouchTarget都会被重置为NUll,这时就会给处理了Down事件的子view去处理后续的事件。 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. // 在cancel、up的状态下重置触摸状态。 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; } 复制代码