从一个聊天信息引发的思考之Android事件分发机制

     转载请声明:http://www.cnblogs.com/courtier/p/4295235.html

  • 起源:

       我在某一天看到了下面的一条信息(如下图),我想了下(当然不是这个人问的问题)“为什么Activity能够与界面交互和为什么它们

       的事件能够传递起来?”我带着这些疑问,自己上网查阅了一些资料与信息,从而得出以下的原理。

     技术分享

  • Activity Window View 的关系:

        我的归纳:众所周知,Activity并非是真正的显示对象。Activity类有个成员变量叫做:mWindow的类型为Window,而这个才是

        我们显示窗体的类型-Phone Window(PolicyManager.makeNewWindow()创建)。PhoneWindow类有两个重要的成员变量

        mDecor和mContentParent 。其中,mDecor(内部类)是主类,而mContentParent我们加入的View就是放在这里。如下图:

        技术分享           而我们主类(窗口类)如何来改变是由:ViewRoot来决定的。既然,主类(窗口类)依赖于ViewRoot,那么,我们的ViewRoot

           就持有Phone Window的实例(即mDecor)。ViewRoot对象都有一个类型为WindowManager.LayoutParams的成员变量

           mWindowAttributes,它指向了一个ViewGroup.LayoutParams对象,用来描述与该ViewRoot对象对应的一个Activity组件

           的UI布局信息。那么,我们ViewRoot何时知道改变呢?主要取决于:WindowManagerService(负责事件处理)。

           那么,长话短说看个图即可:(IWindowSession是ViewRoot的变量)

          技术分享 (来源于:http://blog.csdn.net/wangjinyu501/article/details/9008271?utm_source=tuicool

  • 事件机制:(有了以上的东西可以明白消息是怎么样传到Activity)

           技术分享

(wms->viewroot->phonewindow.DecorView->Activity->phonewindow->phonewindow.DecorView->ViewGroup)

  • 从ViewGroup开始去理解源代码:
public class FrameLayout extends ViewGroup {
    //......
  
            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
            //disallowIntercept:表明如果子控件请求父控件不要拦截事件。
           //调用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;
            }
          //..
         //如果,拦截了就是MotionEvent.ACTION_CANCEL
         final boolean canceled = resetCancelNextUpFlag(this)
         || actionMasked == MotionEvent.ACTION_CANCEL;
         //不拦截就处理ActionDown
        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 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 (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.
           //上面执行的方法不会执行的条件就是:
           //1:你的动作不是ActionDown
          //2:已经启动拦截事件
            if (mFirstTouchTarget == null) {
            //mFirstTouchTarget==null:只有两种情况:
           //第一种:上面有拦截
          //第二种:OnTouch事件没有被消费(即子类没有能力消费父类的)也就是说处理了ACTION_DOWN事件后,发现没办法处理才返回
                // No touch targets so treat this as an ordinary view.
                //dispatchTransformedTouchEvent:重新调用父类类
                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;
                }
            }
    //....ViewGrouo结束
}
  • View开始去理解源代码:(ViewGroup下面的各种View)
public boolean dispatchTouchEvent(MotionEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
           //li.mOnTouchListener.onTouch:就是我们经常调用的OnTouchListener,返回值:
           //True:不会执行我们的(onTouchEvent就是我们的Onclicklistener方法了
          //False:就会执行Onclicklistener
            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;
    }
  • 总结:

        1:关于拦截事件:

                 问题1:为什么要有拦截事件(思考如下):我觉得是因为两个事件(父子View)有冲突,系统执行哪个存在疑问。拦截掉子类的。

                  拦截事件有几个重要点:1:被拦截后子控件收到的都是ACTION_CANCEL。2:父类一次拦截终身拦截。

        2:OnTouchListener 和 OnClickListener:

                  问题2:为什么执行完OnTouchListener 返回真后就不能执行下面的了?我觉得是因为,两个事件具有重合性。Touch事件:已经

                  包含了有(Click的性质方面)。

      3:参考很多高手的文章(十几篇吧)在此表示感谢,让我学习完豁然开朗,有写错的请留言。谢谢。新年快乐!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。