zoukankan      html  css  js  c++  java
  • 从一个聊天信息引发的思考之Android事件分发机制

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

    • 起源:

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

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

         QQ图片20150216165215

    • Activity Window View 的关系:

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

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

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

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

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

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

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

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

              1366614231_7755 (来源于: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:参考很多高手的文章(十几篇吧)在此表示感谢,让我学习完豁然开朗,有写错的请留言。谢谢。新年快乐!

  • 相关阅读:
    Discuz 网站移至 Ubuntu 14.04.4 LTS VPS 配置
    ubuntu的一些常用命令,测试版本:Ubuntu 12.04.5 LTS
    文本的水平对齐方式,带来的一系列问题
    庆祝我的Blog开张了,今天起和大家一起见证一款原生的富文本编辑组件FoxJsonRichView的诞生过程。
    竖直导航(一)
    测试
    第七次作业
    第六次作业
    第四次作业
    第五次作业
  • 原文地址:https://www.cnblogs.com/courtier/p/4295235.html
Copyright © 2011-2022 走看看