Android 事件传递

来源:互联网 发布:淘宝美工怎么上架图片 编辑:程序博客网 时间:2024/05/01 18:29

Android 事件传递

看了很多的博客都没有涉及到源码的层面来讲解事件传递,看完都感觉有点理解又有点疑惑,所以自己整理一篇。

我是从Activity的dispatchTouchEvent方法入手的,至于Activity的dispatchTouchEvent方法是怎么被调用的,具体是在WindowManagerService中,有兴趣的可以自己去了解。

代码流程:

一般我们点击屏幕之后,WindowManagerService会获取到Touch事件,并将该Touch事件派发给Activity,也就是会调用Activity的dispatchTouchEvent方法。具体的可以看代码。

代码都是从Android 5.1.1中截出来的

Activity的dispatchTouchEvent方法:

/*** Called to process touch screen events. You can override this to* intercept all touch screen events before they are dispatched to the* window.  Be sure to call this implementation for touch screen events* that should be handled normally.** @param ev The touch screen event.** @return boolean Return true if this event was consumed.*/public boolean dispatchTouchEvent(MotionEvent ev) {    if (ev.getAction() == MotionEvent.ACTION_DOWN) {        //onUserInteraction是一个空函数,什么都没做        onUserInteraction();    }    //getWindow获取到的是PhoneWindow类    if (getWindow().superDispatchTouchEvent(ev)) {        return true;    }    //如果superDispatchTouchEvent返回false,则调用Activity的onTouchEvent方法。    return onTouchEvent(ev);}

从getWindow().superDispatchTouchEvent(ev)这里可以看到,Activity又将事件派发给PhoneWindow来处理。
如果superDispatchTouchEvent返回false,则会调用Activity的OnTouchEvent方法。
我们接着看一下PhoneWindow的代码。

@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {    return mDecor.superDispatchTouchEvent(event);}

这个方法很简单,又将事件传给mDecor来处理,mDecor是一个DecorView对象,DecorView又继承了FrameLayout。

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {     public boolean superDispatchTouchEvent(MotionEvent event) {        //这里又调用了父类的dispatchTouchEvent,        //也就是FrameLayout的dispatchTouchEvent方法。        return super.dispatchTouchEvent(event);    }}

FrameLayout没有重写dispatchTouchEvent方法,因此调用的是ViewGroup的dispatchTouchEvent方法。该方法代码比较多,慢慢分析。需要注意TouchTarget,TouchTarget主要是用来记录事件接收情况,包括接收该事件的子view、事件详情等。dispatchTouchEvent就是根据TouchTarget来决定派发事件到哪些子View中。

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {    ......    //最后会返回这个handled    boolean handled = false;    //应用安全策略来过滤一部分事件,比如View被遮挡了就会被过滤    if (onFilterTouchEventForSecurity(ev)) {        final int action = ev.getAction();        final int actionMasked = action & MotionEvent.ACTION_MASK;        // 处理初始的Down事件,只要收到Down事件,就认为是一个新的Touch事件        if (actionMasked == MotionEvent.ACTION_DOWN) {            cancelAndClearTouchTargets(ev);            resetTouchState();        }        //检查是否拦截,默认false        //mFirstTouchTarget 是TouchTarget链表的头节点。        final boolean intercepted;        if (actionMasked == MotionEvent.ACTION_DOWN                || mFirstTouchTarget != null) {             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;            //如果允许拦截            if (!disallowIntercept) {                //这里调用了onInterceptTouchEvent方法,                //该方法默认返回false,即不进行拦截                intercepted = onInterceptTouchEvent(ev);                ev.setAction(action);            } else {                intercepted = false;            }        } else {            intercepted = true;        }        // 检查cancel事件.        final boolean canceled = resetCancelNextUpFlag(this)                || actionMasked == MotionEvent.ACTION_CANCEL;        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;        TouchTarget newTouchTarget = null;        boolean alreadyDispatchedToNewTouchTarget = false;        if (!canceled && !intercepted) {//判断是否取消或者被拦截            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()                      ? findChildWithAccessibilityFocus() : null;            //只有DOWN事件(包括多点触控)才会走这个流程,            //这个流程主要是找到能接受该MotionEvent的TouchTarget            //后续的其他事件则直接跳过该流程            if (actionMasked == MotionEvent.ACTION_DOWN                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {                final int actionIndex = ev.getActionIndex(); // always 0 for down                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)                        : TouchTarget.ALL_POINTER_IDS;                //Removes the pointer ids from consideration.                removePointersFromTouchTargets(idBitsToAssign);                final int childrenCount = mChildrenCount;                if (newTouchTarget == null && childrenCount != 0) {                    final float x = ev.getX(actionIndex);                    final float y = ev.getY(actionIndex);                    //由前往后遍历所有的子View,                    //寻找一个能接受该事件的子view,并生成对应的TouchTarget                    final ArrayList<View> preorderedList = buildOrderedChildList();                    final boolean customOrder = preorderedList == null                            && isChildrenDrawingOrderEnabled();                    final View[] children = mChildren;                    for (int i = childrenCount - 1; i >= 0; i--) {                        final int childIndex = customOrder                                ? getChildDrawingOrder(childrenCount, i) : i;                        final View child = (preorderedList == null)                                ? children[childIndex] : preorderedList.get(childIndex);                        // If there is a view that has accessibility focus we want it                        // to get the event first and if not handled we will perform a                        // normal dispatch. We may do a double iteration but this is                        // safer given the timeframe.                        if (childWithAccessibilityFocus != null) {                            if (childWithAccessibilityFocus != child) {                                continue;                            }                            childWithAccessibilityFocus = null;                            i = childrenCount - 1;                        }                        //判断该view是否能接受事件且事件坐标不超过view范围                        if(!canViewReceivePointerEvents(child)                                || !isTransformedTouchPointInView(x, y, child, null)) {                            continue;                        }                        //获取child对应的TouchTarget,                        //如果child还没有对应的TouchTarget,则返回null                        newTouchTarget = getTouchTarget(child);                        // 若子View已经在TouchTarget链表中                        if (newTouchTarget != null) {                            // 将事件id加入到touchTarget中                            newTouchTarget.pointerIdBits |= idBitsToAssign;                            break;                        }                        resetCancelNextUpFlag(child);                        //dispatchTransformedTouchEvent这个方法会把事件传递给子View,                        //方法内部会调用子View的dispatchTouchEvent方法。                        //这个方法后面会讲到。                        //并返回子View的dispatchTouchEvent的返回值。                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {                            //为true代表子View接收了该事件                            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();                            //重新生成一个TouchTarget,包含了子view                            newTouchTarget = addTouchTarget(child, idBitsToAssign);                            //标记为已经将事件分发,避免后面重新分发Down事件。                            alreadyDispatchedToNewTouchTarget = true;                            //已经找到TouchTarget了,结束遍历                            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();                }                // 没有发现接收事件的子View                if (newTouchTarget == null && mFirstTouchTarget != null) {                    // 把事件赋给最早的TouchTarget                    newTouchTarget = mFirstTouchTarget;                    while (newTouchTarget.next != null) {                        newTouchTarget = newTouchTarget.next;                    }                    newTouchTarget.pointerIdBits |= idBitsToAssign;                }        }        //将事件派发给TouchTarget        if (mFirstTouchTarget == null) {            //没有TouchTarget,所以派发给自己            handled = dispatchTransformedTouchEvent(ev, canceled, null,                    TouchTarget.ALL_POINTER_IDS);        } else {            // 派发事件给TouchTarget            TouchTarget predecessor = null;            TouchTarget target = mFirstTouchTarget;            //遍历TouchTarget链表            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;            }       }       //如果是UP或者Cancel事件,则清空TouchTarget链表       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;}

从上面的代码可以看到,对事件的派发工作都是在dispatchTransformedTouchEvent方法中。
dispatchTransformedTouchEvent会将事件的坐标转换为特定子view中的坐标,并调用子view的dispatchTouchEvent方法,最后返回dispatchTouchEvent的返回值。

/** * 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;    final int oldAction = event.getAction();    //取消事件不需要转换坐标或者过滤,因为取消事件不需要对坐标进行处理。    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {        event.setAction(MotionEvent.ACTION_CANCEL);        //派发cancel事件        if (child == null) {            //注意是调用super的dispatchTouchEvent方法            //即View的dispatchTouchEvent方法            handled = super.dispatchTouchEvent(event);        } else {            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) {                //注意是调用super的dispatchTouchEvent方法                handled = super.dispatchTouchEvent(event);            } else {                //坐标转换                final float offsetX = mScrollX - child.mLeft;                final float offsetY = mScrollY - child.mTop;                event.offsetLocation(offsetX, offsetY);                //分发给子View                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());        }        //派发给子view        handled = child.dispatchTouchEvent(transformedEvent);    }    // Done.    transformedEvent.recycle();    return handled;}

到这里就把事件从Activity分发到View上中。
接下来看看View的dispatchTouchEvent方法。

/** * 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) {    //最后会返回这个值    boolean result = false;    ......    final int actionMasked = event.getActionMasked();    if (actionMasked == MotionEvent.ACTION_DOWN) {        // Defensive cleanup for new gesture        stopNestedScroll();    }    //应用安全策略来过滤一部分事件,比如View被遮挡了就会被过滤    if (onFilterTouchEventForSecurity(event)) {        //ListenerInfo类含有View的很多监听事件,包括OnTouchListener、OnClickListener等。        ListenerInfo li = mListenerInfo;        //这里执行li.mOnTouchListener.onTouch方法,也就是执行我们通过setOnTouchListener的监听事件。        if (li != null && li.mOnTouchListener != null            && (mViewFlags & ENABLED_MASK) == ENABLED            && li.mOnTouchListener.onTouch(this, event)) {            result = true;        }        //这里要注意,如果我们设置的TouchListener返回false,则还会执行View自身的OnTouchEvent事件。        if (!result && onTouchEvent(event)) {            result = true;        }    }    if (!result && mInputEventConsistencyVerifier != null) {        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);    }    // Clean up after nested scrolls if this is the end of a gesture;    // also cancel it if we tried an ACTION_DOWN but we didn't want the rest    // of the gesture.    if (actionMasked == MotionEvent.ACTION_UP ||        actionMasked == MotionEvent.ACTION_CANCEL ||        (actionMasked == MotionEvent.ACTION_DOWN && !result)) {        //停止滚动        stopNestedScroll();    }    return result;}

TouchTarget

TouchTarget的定义和相关方法

/* Describes a touched view and the ids of the pointers that it has captured. * * This code assumes that pointer ids are always in the range 0..31 such that * it can use a bitfield to track which pointer ids are present. * As it happens, the lower layers of the input dispatch pipeline also use the * same trick so the assumption should be safe here... */private static final class TouchTarget {    private static final int MAX_RECYCLED = 32;    private static final Object sRecycleLock = new Object[0];    private static TouchTarget sRecycleBin;    private static int sRecycledCount;    public static final int ALL_POINTER_IDS = -1; // all ones    // The touched child view.    public View child;    // The combined bit mask of pointer ids for all pointers captured by the target.    public int pointerIdBits;    // The next target in the target list.    public TouchTarget next;    private TouchTarget() {    }    public static TouchTarget obtain(View child, int pointerIdBits) {        final TouchTarget target;        synchronized (sRecycleLock) {            if (sRecycleBin == null) {                target = new TouchTarget();            } else {                target = sRecycleBin;                sRecycleBin = target.next;                sRecycledCount--;                target.next = null;            }        }        target.child = child;        target.pointerIdBits = pointerIdBits;        return target;    }    public void recycle() {        synchronized (sRecycleLock) {            if (sRecycledCount < MAX_RECYCLED) {                next = sRecycleBin;                sRecycleBin = this;                sRecycledCount += 1;            } else {                next = null;            }            child = null;        }    }}
/** * Resets all touch state in preparation for a new cycle. */private void resetTouchState() {    clearTouchTargets();    resetCancelNextUpFlag(this);    mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;    mNestedScrollAxes = SCROLL_AXIS_NONE;}/**    * Resets the cancel next up flag. Returns true if the flag was previously set. */private static boolean resetCancelNextUpFlag(View view) {    if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {            view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;        return true;    }    return false;}/** * Clears all touch targets. */private void clearTouchTargets() {    TouchTarget target = mFirstTouchTarget;    if (target != null) {        do {            TouchTarget next = target.next;            target.recycle();            target = next;        } while (target != null);        mFirstTouchTarget = null;   }}/**    * Cancels and clears all touch targets. */private void cancelAndClearTouchTargets(MotionEvent event) {    if (mFirstTouchTarget != null) {            boolean syntheticEvent = false;        if (event == null) {            final long now = SystemClock.uptimeMillis();            event = MotionEvent.obtain(now, now,            MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);            syntheticEvent = true;        }        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {            resetCancelNextUpFlag(target.child);            dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);        }        clearTouchTargets();        if (syntheticEvent) {            event.recycle();        }    }}/**   * Gets the touch target for specified child view. Returns null if not found. */private TouchTarget getTouchTarget(View child) {    for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {        if (target.child == child) {            return target;        }    }    return null;}/**   * 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;}/** * Removes the pointer ids from consideration. */private void removePointersFromTouchTargets(int pointerIdBits) {    TouchTarget predecessor = null;    TouchTarget target = mFirstTouchTarget;    while (target != null) {        final TouchTarget next = target.next;        if ((target.pointerIdBits & pointerIdBits) != 0) {            target.pointerIdBits &= ~pointerIdBits;            if (target.pointerIdBits == 0) {                if (predecessor == null) {                    mFirstTouchTarget = next;                } else {                    predecessor.next = next;                }                target.recycle();                target = next;                continue;            }        }        predecessor = target;        target = next;    }}private void cancelTouchTarget(View view) {    TouchTarget predecessor = null;    TouchTarget target = mFirstTouchTarget;    while (target != null) {        final TouchTarget next = target.next;        if (target.child == view) {            if (predecessor == null) {                mFirstTouchTarget = next;            } else {                predecessor.next = next;            }            target.recycle();            final long now = SystemClock.uptimeMillis();            MotionEvent event = MotionEvent.obtain(now, now,            MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);            view.dispatchTouchEvent(event);            event.recycle();            return;        }        predecessor = target;        target = next;    }}

总结:

Activity的传递

  1. WindowManagerService将事件传给Activity。
  2. Activity调用dispatchTouchEvent()方法分发事件。
    1. 如果是Down事件,会调用onUserInteraction方法。
    2. 通过PhoneWindow把事件分发给Activity最底层的ViewGroup。
    3. 如果最底层的ViewGroup返回false,则调用Activity的onTouchEvent。

ViewGroup的分发过程

  1. Activity将事件传给最底层的ViewGroup,调用ViewGroup的dispatchTouchEvent()方法。
  2. 如果接受到Down事件,则代表是一个新的Touch事件,会进行初始化,并清除之前的所有状态。
  3. ViewGroup调用onInterceptTouchEvent判断是否拦截事件分发。
  4. 如果没有被拦截,且为Down事件,则遍历所有的子View。
    1. 如果子view不能接受事件,或者事件的坐标不在子View的范围内,则跳过。
    2. 如果子View已经有对应TouchTarget对象,则由那个TouchTarget分发事件。
    3. 调用dispatchTransformedTouchEvent将事件传给子View。
      • 返回true,表示该View接受了该事件,会生成对应的TouchTarget,并加入到TouchTarget链表中。
      • 返回false,表示该View不接受该事件。
  5. 根据TouchTarget链表分发事件。
    • TouchTarget链表为空,则调用super.dispatchTouchEvent(),将事件分发给自己。
    • 否则遍历TouchTarget链表,并分别调用dispatchTransformedTouchEvent进行分发事件。
      • 如果child的参数是空的,则代表是调用给自身的dispatchTouchEvent()。
      • 如果child的参数不为空,则调用child的dispatchTouchEvent()。

View

  1. ViewGroup通过调用View的dispatchTouchEvent方法将事件分发给View。
  2. 如果View设置了OnTouchListener,则调用OnTouchListener的onTouch方法。
    • onTouch返回true,则dispatchTouchEvent返回true。
  3. View没有设置OnTouchListener事件或者onTouch返回false,则调用View的onTouchEvent方法,如果onTouchEvent方法返回true,dispatchTouchEvent也会返回true。
  4. dispatchTouchEvent返回true,则表示该事件已经被接受。
0 0
原创粉丝点击