澳门新葡萄京官网首页viewgroup的touch事件处理机制

有些没聊到但也比较重视的点

地点表明的事物都相当的粗略,是从八个ViewGroup+一个View开首的,事件分发的奉行者是ViewGroup,子容器也只有叁个View,但其实支出中本来没这么轻便,可是不用怕,再复杂的景色也能够拆分成这种情势的,只但是档期的顺序多了部分递归复杂了部分罢了,原理如故长期以来的。

顺手补充几点:

  • 从顾客点击荧屏开端接触叁个雨后春笋的点击事件时,事件真的的传递流程是:Activity(PhoneWindow)->DecorView->ViewGroup->View,在达到ViewGroup在此之前还恐怕有七个DecorView,事件是从Activity传过来的,但那一个东西其实和ViewGroup的规律是相近的,Activity能作为多个大的ViewGroup,当它的DecorView富含的保有子View未有人能够消耗事件的时候(那样说有尾巴,大家懂笔者的意味就能够了State of Qatar最后仍旧会提交Activity管理。
  • 事件冲突解决能够坚决守护地方的规律在多少个point中实行拍卖。最轻巧想到的管理的火候是在onInterceptTouch伊芙nt里,举个例子当三个竖直方向滑动的ViewGroup里嵌套二个横向滑动的ViewGroup,能够在这里地的ACTION_MOVE里来剖断后续事件应该传递给什么人管理,当然,也能够依赖地点说的标识位FLAG_DISALLOW_INTERCEPT合作子View的dispatchTouch伊夫nt来支配事件的流向,那都是比较容易想到的,不过看过别的大神,通过分享Motion伊夫nt的措施来调节事件的流向,即在父容器中保存Motion伊芙nt并在适度的机会传入子View自定义的事件管理方法来享受事件,也是卓有功能的。
  • 其他View只要拒绝了一类别事件中的ACTION_DOWN(再次来到false),则持续事件都不会再传递过来了。但借使谢绝了此外的风浪,后续事件依旧得以传过来的,举个例子View某次ACTION_MOVE没管理,那个没管理的事件结尾会被Activity消耗掉(实际不是View的父容器),但持续的平地风波或许会一而再一连传给该View。
  • 客观的应用ACTION_CANCEL能够决定四个五颜六色事件的生命周期,让事件管理更灵敏。

精晓事件分发的编写制定只要通晓上面的规律基本就丰富了,github上过多屌炸天的大神写的种种璀璨的自定义控件的事分发依照那几个也能够看精晓,当然还大概有非常多扩展的事物和更加尖锐的剧情由于篇幅的涉嫌在这里处就不罗嗦了,更注重的还是去看源码吧。
提及底送各位一句优越:偶一为之,绝知那件事要躬行!

true,表达ViewGroup管理了这几个点击事件(ViewGroup本身照旧子View处理的),何况那么些体系的点击事件会继续传布那些ViewGroup来管理,若isConsume

false(ACTION_DOWN时),ViewGroup不能管理那一个点击事件,那么那几个系类的点击事件就和该ViewGroup无缘了。会把这些事件上抛给协和的父容器恐怕Activity管理。

伪代码说罢了,ViewGroup的事件传递法则也就基本上说罢了,这么看是或不是很简短了。View相对于ViewGroup来讲就更简便易行了,未有阻拦方法,dispatch基本上是直接调用了自家的onTouch伊夫nt,管理起来一点难度都木有啊。

Android事件分发详细明白(三卡塔尔(قطر‎——ViewGroup的dispatchTouch伊夫nt(State of Qatar源码学习

分类:
Android
Android进级学习
2014-12-29 18:00 311人阅读 评论(1)
收藏

举报

[java] view
plaincopyprint?澳门新葡萄京官网首页 1澳门新葡萄京官网首页 2

  1. package cc.aa;  
  2.   
  3. import android.os.Environment;  
  4. import android.view.MotionEvent;  
  5. import android.view.View;  
  6.   
  7. public class UnderstandDispatchTouchEvent {  
  8.     /** 
  9.      * dispatchTouch伊夫nt(State of Qatar源码学习及其注释 
  10.      * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouch伊芙nt->onTouchEvent 
  11.      * 在这里个链条中dispatchTouch伊芙nt(卡塔尔(قطر‎是处于链首的岗位当然也是最关键的. 
  12.      * 在dispatchTouchEvent(State of Qatar决定了Touch事件是由友好的onTouchEvent(卡塔尔管理 
  13.      * 依旧散发给子View管理让子View调用其自己的dispatchTouchEvent(卡塔尔(قطر‎管理. 
  14.      *  
  15.      *  
  16.      * 其实dispatchTouchEvent()和onInterceptTouchEvent()以及onTouchEvent()的关系 
  17.      * 在dispatchTouchEvent(卡塔尔方法的源码中反映得很显然. 
  18.      * 比如dispatchTouchEvent(卡塔尔国会调用onInterceptTouchEvent(卡塔尔国来判别是还是不是要拦截. 
  19.      * 比方dispatchTouch伊芙nt(State of Qatar会调用dispatchTransformedTouch伊夫nt(卡塔尔方法且在该办法中递归调用 
  20.      * dispatchTouch伊芙nt(卡塔尔(قطر‎;从而会在dispatchTouch伊夫nt(卡塔尔(قطر‎里最终调用到onTouchEvent(卡塔尔 
  21.      *  
  22.      *  
  23.      *  
  24.      * 器重关心: 
  25.      * 1 子View对于ACTION_DOWN的管理特别要害!!澳门新葡萄京官网首页,!!! 
  26.      *   ACTION_DOWN是一比比皆已经Touch事件的先河,借使子View对于该ACTION_DOWN事件在onTouch伊夫nt(卡塔尔中回到了false即未开销. 
  27.      *   那么ViewGroup就不会把后续的ACTION_MOVE和ACTION_UP派发给该子View.在此种气象下ViewGroup就和多如牛毛的View相通了, 
  28.      *   调用该ViewGroup自个儿的dispatchTouch伊夫nt(卡塔尔(قطر‎进而调用自个儿的onTouchEvent(State of Qatar;即不会将事件分发给子View. 
  29.      *   详细代码请参见如下代码分析. 
  30.      *    
  31.      * 2 为何子view对于Touch事件管理再次来到true那么其上层的ViewGroup就不能处理Touch事件了????? 
  32.      *   这一个也许大家都驾驭了,因为该Touch事件被子View花费了其上层的ViewGroup就不也许处理该Touch事件了. 
  33.      *   那么在源码中的凭仗是何许吧??请看下边包车型客车源码深入分析 
  34.      *    
  35.      * 参谋资料: 
  36.      * 0  
  37.      * 1  
  38.      * 2  
  39.      * 3  
  40.      * 4  
  41.      * 5  
  42.      * 6  
  43.      *   Thank you very much 
  44.      */  
  45.       
  46.     @Override  
  47.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  48.         if (mInputEventConsistencyVerifier != null) {  
  49.             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);  
  50.         }  
  51.   
  52.         boolean handled = false;  
  53.         if (onFilterTouchEventForSecurity(ev)) {  
  54.             final int action = ev.getAction();  
  55.             final int actionMasked = action & MotionEvent.ACTION_MASK;  
  56.   
  57.             /** 
  58.              * 第一步:对于ACTION_DOWN实行管理(Handle an initial down卡塔尔(قطر‎ 
  59.              * 因为ACTION_DOWN是一精彩纷呈事件的发端,当是ACTION_DOWN时举行部分先河化操作. 
  60.              * 从源码的注释也能够看出来:消灭未来的Touch状态(state卡塔尔开首新的手势(gesture卡塔尔(قطر‎ 
  61.              * cancelAndClearTouchTargets(evState of Qatar中有二个百般首要的操作: 
  62.              * 将mFirstTouchTarget设置为null!!!! 
  63.              * 随后在resetTouchState(State of Qatar中重置Touch状态标志 
  64.              */  
  65.             if (actionMasked == MotionEvent.ACTION_DOWN) {  
  66.                 // Throw away all previous state when starting a new touch gesture.  
  67.                 // The framework may have dropped the up or cancel event for the previous gesture  
  68.                 // due to an app switch, ANR, or some other state change.  
  69.                 cancelAndClearTouchTargets(ev);  
  70.                 resetTouchState();  
  71.             }  
  72.   
  73.               
  74.             /** 
  75.              * 第二步:检查是还是不是要堵住(Check for interception卡塔尔国 
  76.              * 在dispatchTouchEvent(MotionEventevState of Qatar这段代码中 
  77.              * 使用变量intercepted来标志ViewGroup是不是拦截Touch事件的传递. 
  78.              * 该变量在那起彼伏代码中起着很关键的效果. 
  79.              */  
  80.             final boolean intercepted;  
  81.             // 事件为ACTION_DOWN只怕mFirstTouchTarget不为null(即现已找到能够吸取touch事件的目的组件卡塔尔(قطر‎时if创制  
  82.             if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {  
  83.                 //推断disallowIntercept(禁绝拦截卡塔尔(قطر‎标识位  
  84.                 //因为在其余地方只怕调用了requestDisallowInterceptTouch伊夫nt(boolean disallowIntercept卡塔尔  
  85.                 //进而禁绝实行是还是不是须要拦截的论断(有一些刚毅~其实看requestDisallowInterceptTouchEvent(卡塔尔方法名就可清楚卡塔尔(قطر‎  
  86.                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;  
  87.                 //当未有明确命令制止拦截判定时(即disallowIntercept为false卡塔尔(قطر‎调用onInterceptTouchEvent(ev卡塔尔国方法  
  88.                 if (!disallowIntercept) {  
  89.                     //既然disallowIntercept为false那么就调用onInterceptTouch伊夫nt(卡塔尔国方法将结果赋值给intercepted  
  90.                     //常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouch伊芙nt->onTouch伊芙nt  
  91.                     //其实在这里正是三个反映,在dispatchTouchEvent(卡塔尔国中调用了onInterceptTouchEvent(State of Qatar  
  92.                     intercepted = onInterceptTouchEvent(ev);  
  93.                     ev.setAction(action); // restore action in case it was changed  
  94.                 } else {  
  95.                      //当幸免拦截判定时(即disallowIntercept为true卡塔尔设置intercepted = false  
  96.                     intercepted = false;  
  97.                 }  
  98.             } else {  
  99.                 //当事件不是ACTION_DOWN并且mFirstTouchTarget为null(即没有Touch的靶子组件)时  
  100.                 //设置 intercepted = true代表ViewGroup施行Touch事件拦截的操作。  
  101.                 //There are no touch targets and this action is not an initial down  
  102.                 //so this view group continues to intercept touches.  
  103.                 intercepted = true;  
  104.             }  
  105.   
  106.               
  107.             /** 
  108.              * 第三步:检查cancel(Check for cancelation) 
  109.              *  
  110.              */  
  111.             final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;  
  112.   
  113.               
  114.             /** 
  115.              * 第四步:事件分发(Update list of touch targets for pointer down, if needed卡塔尔 
  116.              */  
  117.             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;  
  118.             TouchTarget newTouchTarget = null;  
  119.             boolean alreadyDispatchedToNewTouchTarget = false;  
  120.             //不是ACTION_CANCEL何况ViewGroup的阻止标记位intercepted为false(不阻止卡塔尔国  
  121.             if (!canceled && !intercepted) {  
  122.                 //处理ACTION_DOWN事件.那些环节比较繁杂.  
  123.                 if (actionMasked == MotionEvent.ACTION_DOWN  
  124.                     || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)  
  125.                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  
  126.                     final int actionIndex = ev.getActionIndex(); // always 0 for down  
  127.                     final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex):TouchTarget.ALL_POINTER_IDS;  
  128.   
  129.                     // Clean up earlier touch targets for this pointer id in case they  
  130.                     // have become out of sync.  
  131.                     removePointersFromTouchTargets(idBitsToAssign);  
  132.   
  133.                     final int childrenCount = mChildrenCount;  
  134.                     if (childrenCount != 0) {  
  135.                         // 借助Touch坐标寻觅子View来接过Touch事件  
  136.                         // Find a child that can receive the event.  
  137.                         // Scan children from front to back.  
  138.                         final View[] children = mChildren;  
  139.                         final float x = ev.getX(actionIndex);  
  140.                         final float y = ev.getY(actionIndex);  
  141.   
  142.                         final boolean customOrder = isChildrenDrawingOrderEnabled();  
  143.                         // 遍历子View判别哪些子View接纳Touch事件  
  144.                         for (int i = childrenCount – 1; i >= 0; i–) {  
  145.                             final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;  
  146.                             final View child = children[childIndex];  
  147.                             if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) {  
  148.                                 continue;  
  149.                             }  
  150.   
  151.                             newTouchTarget = getTouchTarget(child);  
  152.                             if (newTouchTarget != null) {  
  153.                                 // 找到选用Touch事件的子View!!!!!!!即为newTouchTarget.  
  154.                                 // 既然已经找到了,所以举行break跳出for循环  
  155.                                 // Child is already receiving touch within its bounds.  
  156.                                 // Give it the new pointer in addition to the ones it is handling.  
  157.                                 newTouchTarget.pointerIdBits |= idBitsToAssign;  
  158.                                 break;  
  159.                             }  
  160.   
  161.                             resetCancelNextUpFlag(child);  
  162.                             /** 
  163.                              * 假若上边的if不满意,当然也不会奉行break语句. 
  164.                              * 于是代码会施行到此处来. 
  165.                              *  
  166.                              * 调用艺术dispatchTransformedTouchEvent(卡塔尔(قطر‎将Touch事件传递给子View做 
  167.                              * 递归管理(约等于遍历该子View的View树卡塔尔国 
  168.                              * 该方法很关键,看一下源码中有关该办法的叙述: 
  169.                              * Transforms a motion event into the coordinate space of a particular child view, 
  170.                              * filters out irrelevant pointer ids, and overrides its action if necessary. 
  171.                              * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 
  172.                              * 将Touch事件传递给一定的子View. 
  173.                              * 该方式丰富生死攸关!!!!在该措施中为三个递归调用,会递归调用dispatchTouchEvent(卡塔尔国方法!!!!!!!!!!!!!! 
  174.                              * 在dispatchTouchEvent()中: 
  175.                              * 假若实View为ViewGroup何况Touch未有被截留那么递归调用dispatchTouch伊夫nt(卡塔尔 
  176.                              * 假如实View为View那么就能够调用其onTouch伊芙nt(State of Qatar,这么些就不再赘言了. 
  177.                              *  
  178.                              *  
  179.                              * 该情势重返true则代表子View花费掉该事件,同时跻身该if决断. 
  180.                              * 满意if语句后根本的操作有: 
  181.                              * 1 给newTouchTarget赋值 
  182.                              * 2 给alreadyDispatchedToNewTouchTarget赋值为true. 
  183.                              *   看那一个相比长的Turkey语名字也可以知道其意义:已经将Touch派发给新的TouchTarget 
  184.                              * 3 执行break. 
  185.                              *   因为该for循环遍历子View推断哪些子View选择Touch事件,既然已经找到了 
  186.                              *   那么就跳出该for循环. 
  187.                              * 4 注意: 
  188.                              *   如果dispatchTransformedTouchEvent()返回false即子View 
  189.                              *   的onTouch伊芙nt重返false(即Touch事件未被开支State of Qatar那么就不满意该if条件,也就不能够履行addTouchTarget(State of Qatar 
  190.                              *   进而以致mFirstTouchTarget为null.那么该子View就不可能继续管理ACTION_MOVE事件 
  191.                              *   和ACTION_UP事件!!!!!!!!!!!!!!!!!!!!!! 
  192.                              * 5 注意: 
  193.                              *   如果dispatchTransformedTouchEvent()返回true即子View 
  194.                              *   的onTouch伊夫nt再次来到true(即Touch事件被费用卡塔尔(قطر‎那么就满意该if条件. 
  195.                              *   从而mFirstTouchTarget不为null!!!!!!!!!!!!!!!!!!! 
  196.                              * 6 小结: 
  197.                              *   对于此处ACTION_DOWN的管理具体体未来dispatchTransformedTouchEvent(卡塔尔(قطر‎ 
  198.                              *   该方法再次回到boolean,如下: 
  199.                              *   true—->事件被开销—–>mFirstTouchTarget!=null 
  200.                              *   false—>事件未被花费—->mFirstTouchTarget==null 
  201.                              *   因为在dispatchTransformedTouch伊芙nt(卡塔尔(قطر‎会调用递归调用dispatchTouchEvent(卡塔尔国和onTouchEvent(State of Qatar 
  202.                              *   所以dispatchTransformedTouchEvent(卡塔尔(قطر‎的重回值实际上是由onTouchEvent(State of Qatar决定的. 
  203.                              *   轻便地说onTouch伊芙nt(卡塔尔(قطر‎是或不是成本了Touch事件(true or false卡塔尔国的重临值决定了dispatchTransformedTouch伊芙nt(State of Qatar 
  204.                              *   的重临值!!!!!!!!!!!!!进而调控了mFirstTouchTarget是或不是为null!!!!!!!!!!!!!!!!进而进一层决定了ViewGroup是不是 
  205.                              *   管理Touch事件.这点在底下的代码中很有体现. 
  206.                              *    
  207.                              *  
  208.                              */  
  209.                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {  
  210.                                 // Child wants to receive touch within its bounds.  
  211.                                 mLastTouchDownTime = ev.getDownTime();  
  212.                                 mLastTouchDownIndex = childIndex;  
  213.                                 mLastTouchDownX = ev.getX();  
  214.                                 mLastTouchDownY = ev.getY();  
  215.                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);  
  216.                                 alreadyDispatchedToNewTouchTarget = true;  
  217.                                 break;  
  218.                             }  
  219.                         }  
  220.                     }  
  221.   
  222.                       
  223.                     /** 
  224.                      * 该if条件表示: 
  225.                      * 经过前面的for循环未有找到子View选拔Touch事件同有时间早先的mFirstTouchTarget不为空 
  226.                      */  
  227.                     if (newTouchTarget == null && mFirstTouchTarget != null) {  
  228.                         // Did not find a child to receive the event.  
  229.                         // Assign the pointer to the least recently added target.  
  230.                         newTouchTarget = mFirstTouchTarget;  
  231.                         while (newTouchTarget.next != null) {  
  232.                             newTouchTarget = newTouchTarget.next;  
  233.                         }  
  234.                         //newTouchTarget指向了最先的TouchTarget  
  235.                         newTouchTarget.pointerIdBits |= idBitsToAssign;  
  236.                     }  
  237.                 }  
  238.             }  
  239.   
  240.               
  241.               
  242.             /** 
  243.              * 分发Touch事件至target(Dispatch to touch targets) 
  244.              *  
  245.              * 经过位置对于ACTION_DOWN的拍卖后mFirstTouchTarget有二种状态: 
  246.              * 1 mFirstTouchTarget为null 
  247.              * 2 mFirstTouchTarget不为null 
  248.              *  
  249.              * 当然倘若不是ACTION_DOWN就不会经过地方较麻烦的流程 
  250.              * 而是从这里最早执行,举例ACTION_MOVE和ACTION_UP 
  251.              */  
  252.             if (mFirstTouchTarget == null) {  
  253.                 /** 
  254.                  * 情况1:mFirstTouchTarget为null 
  255.                  *  
  256.                  * 经过地点的解析mFirstTouchTarget为null正是说Touch事件未被成本. 
  257.                  * 即未有找到能够花费touch事件的子组件或Touch事件被阻挡了, 
  258.                  * 则调用ViewGroup的dispatchTransformedTouch伊夫nt(卡塔尔(قطر‎方法管理Touch事件则和日常View同样. 
  259.                  * 即子View未有花费Touch事件,那么子View的上层ViewGroup才会调用其onTouch伊夫nt(State of Qatar管理Touch事件. 
  260.                  * 在源码中的注释为:No touch targets so treat this as an ordinary view. 
  261.                  * 也等于说那个时候ViewGroup像一个日常的View这样调用dispatchTouchEvent(State of Qatar,且在dispatchTouch伊夫nt(卡塔尔(قطر‎ 
  262.                  * 中会去调用onTouch伊夫nt(卡塔尔方法. 
  263.                  * 具体的说正是在调用dispatchTransformedTouchEvent(State of Qatar时第两个参数为null. 
  264.                  * 第八个参数View child为null会做哪些的拍卖吧? 
  265.                  * 请参见下边dispatchTransformedTouchEvent(卡塔尔国的源码解析 
  266.                  *  
  267.                  * 那正是干什么子view对于Touch事件管理重返true那么其上层的ViewGroup就不大概管理Touch事件了!!!!!!!!!! 
  268.                  * 这就是干什么子view对于Touch事件管理再次来到false那么其上层的ViewGroup手艺够拍卖Touch事件!!!!!!!!!! 
  269.                  *  
  270.                  */  
  271.                 handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);  
  272.             } else {  
  273.                 /** 
  274.                  * 情状2:mFirstTouchTarget不为null即找到了能够费用Touch事件的子View且后续Touch事件能够传递到该子View 
  275.                  * 在源码中的注释为: 
  276.                  * Dispatch to touch targets, excluding the new touch target if we already dispatched to it.   
  277.                  * Cancel touch targets if necessary. 
  278.                  */  
  279.                 TouchTarget predecessor = null;  
  280.                 TouchTarget target = mFirstTouchTarget;  
  281.                 while (target != null) {  
  282.                     final TouchTarget next = target.next;  
  283.                     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {  
  284.                         handled = true;  
  285.                     } else {  
  286.                         final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;  
  287.                         //对于非ACTION_DOWN事件后续传递给目的子组件进行管理,照旧是递归调用dispatchTransformedTouchEvent(卡塔尔(قطر‎  
  288.                         if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {  
  289.                             handled = true;  
  290.                         }  
  291.                         if (cancelChild) {  
  292.                             if (predecessor == null) {  
  293.                                 mFirstTouchTarget = next;  
  294.                             } else {  
  295.                                 predecessor.next = next;  
  296.                             }  
  297.                             target.recycle();  
  298.                             target = next;  
  299.                             continue;  
  300.                         }  
  301.                     }  
  302.                     predecessor = target;  
  303.                     target = next;  
  304.                 }  
  305.             }  
  306.   
  307.             /** 
  308.              * 处理ACTION_UP和ACTION_CANCEL 
  309.              * Update list of touch targets for pointer up or cancel, if needed. 
  310.              * 在这关键的操作是还原状态 
  311.              */  
  312.             if (canceled|| actionMasked == MotionEvent.ACTION_UP  
  313.                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {  
  314.                 resetTouchState();  
  315.             } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {  
  316.                 final int actionIndex = ev.getActionIndex();  
  317.                 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);  
  318.                 removePointersFromTouchTargets(idBitsToRemove);  
  319.             }  
  320.         }  
  321.   
  322.         if (!handled && mInputEventConsistencyVerifier != null) {  
  323.             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);  
  324.         }  
  325.         return handled;  
  326.     }  
  327.       
  328.       
  329.       
  330.     //=====================以上为dispatchTouch伊夫nt(卡塔尔国源码分析======================  
  331.       
  332.       
  333.       
  334.     //===============以下为dispatchTransformedTouchEvent(卡塔尔(قطر‎源码解析=================  
  335.       
  336.     /** 
  337.      * 在dispatchTouch伊芙nt(State of Qatar中调用dispatchTransformedTouch伊芙nt(卡塔尔将事件分发给子View管理 
  338.      *  
  339.      * Transforms a motion event into the coordinate space of a particular child view, 
  340.      * filters out irrelevant pointer ids, and overrides its action if necessary. 
  341.      * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 
  342.      *  
  343.      * 在那请器重注意第八个参数:View child 
  344.      * 在dispatchTouchEvent(卡塔尔国中反复调用了dispatchTransformedTouch伊夫nt(State of Qatar,可是有的时候第多个参数为null,临时又不是. 
  345.      * 那么那几个参数是不是为null有怎么着界别吧? 
  346.      * 在如下dispatchTransformedTouchEvent(State of Qatar源码中可知多次对此child是或不是为null的推断,何况均做出如下雷同的操作: 
  347.      * if (child == null) { 
  348.      *       handled = super.dispatchTouchEvent(event); 
  349.      *    } else { 
  350.      *       handled = child.dispatchTouchEvent(event); 
  351.      * } 
  352.      * 那一个代码是哪些意思吧?? 
  353.      * 当child == null时会将Touch事件传递给该ViewGroup本身的dispatchTouch伊夫nt(卡塔尔(قطر‎管理. 
  354.      * 即super.dispatchTouch伊芙nt(event卡塔尔国正如源码中的注释描述的一致: 
  355.      * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. 
  356.      * 当child != null时会调用该子view(当然该view或者是贰个View也恐怕是多少个ViewGroup卡塔尔(قطر‎的dispatchTouchEvent(event卡塔尔(قطر‎管理. 
  357.      * 即child.dispatchTouchEvent(event); 
  358.      *  
  359.      *  
  360.      */  
  361.     private boolean dispatchTransformedTouchEvent(MotionEvent event,boolean cancel,View child,int desiredPointerIdBits) {  
  362.         final boolean handled;  
  363.   
  364.         // Canceling motions is a special case.  We don’t need to perform any transformations  
  365.         // or filtering.  The important part is the action, not the contents.  
  366.         final int oldAction = event.getAction();  
  367.         if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {  
  368.             event.setAction(MotionEvent.ACTION_CANCEL);  
  369.             if (child == null) {  
  370.                 handled = super.dispatchTouchEvent(event);  
  371.             } else {  
  372.                 handled = child.dispatchTouchEvent(event);  
  373.             }  
  374.             event.setAction(oldAction);  
  375.             return handled;  
  376.         }  
  377.   
  378.         // Calculate the number of pointers to deliver.  
  379.         final int oldPointerIdBits = event.getPointerIdBits();  
  380.         final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;  
  381.   
  382.         // If for some reason we ended up in an inconsistent state where it looks like we  
  383.         // might produce a motion event with no pointers in it, then drop the event.  
  384.         if (newPointerIdBits == 0) {  
  385.             return false;  
  386.         }  
  387.   
  388.         // If the number of pointers is the same and we don’t need to perform any fancy  
  389.         // irreversible transformations, then we can reuse the motion event for this  
  390.         // dispatch as long as we are careful to revert any changes we make.  
  391.         // Otherwise we need to make a copy.  
  392.         final MotionEvent transformedEvent;  
  393.         if (newPointerIdBits == oldPointerIdBits) {  
  394.             if (child == null || child.hasIdentityMatrix()) {  
  395.                 if (child == null) {  
  396.                     handled = super.dispatchTouchEvent(event);  
  397.                 } else {  
  398.                     final float offsetX = mScrollX – child.mLeft;  
  399.                     final float offsetY = mScrollY – child.mTop;  
  400.                     event.offsetLocation(offsetX, offsetY);  
  401.   
  402.                     handled = child.dispatchTouchEvent(event);  
  403.   
  404.                     event.offsetLocation(-offsetX, -offsetY);  
  405.                 }  
  406.                 return handled;  
  407.             }  
  408.             transformedEvent = MotionEvent.obtain(event);  
  409.         } else {  
  410.             transformedEvent = event.split(newPointerIdBits);  
  411.         }  
  412.   
  413.         // Perform any necessary transformations and dispatch.  
  414.         if (child == null) {  
  415.             handled = super.dispatchTouchEvent(transformedEvent);  
  416.         } else {  
  417.             final float offsetX = mScrollX – child.mLeft;  
  418.             final float offsetY = mScrollY – child.mTop;  
  419.             transformedEvent.offsetLocation(offsetX, offsetY);  
  420.             if (! child.hasIdentityMatrix()) {  
  421.                 transformedEvent.transform(child.getInverseMatrix());  
  422.             }  
  423.   
  424.             handled = child.dispatchTouchEvent(transformedEvent);  
  425.         }  
  426.   
  427.         // Done.  
  428.         transformedEvent.recycle();  
  429.         return handled;  
  430.     }  
  431.       
  432.       
  433.       
  434.       
  435.       
  436.       
  437.   
  438. }  

    package cc.aa;

    import android.os.Environment;
    import android.view.MotionEvent;
    import android.view.View;

    public class UnderstandDispatchTouchEvent {
    /**

    * dispatchTouchEvent()源码学习及其注释
    * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent
    * 在这个链条中dispatchTouchEvent()是处在链首的位置当然也是最重要的.
    * 在dispatchTouchEvent()决定了Touch事件是由自己的onTouchEvent()处理
    * 还是分发给子View处理让子View调用其自身的dispatchTouchEvent()处理.
    * 
    * 
    * 其实dispatchTouchEvent()和onInterceptTouchEvent()以及onTouchEvent()的关系
    * 在dispatchTouchEvent()方法的源码中体现得很明显.
    * 比如dispatchTouchEvent()会调用onInterceptTouchEvent()来判断是否要拦截.
    * 比如dispatchTouchEvent()会调用dispatchTransformedTouchEvent()方法且在该方法中递归调用
    * dispatchTouchEvent();从而会在dispatchTouchEvent()里最终调用到onTouchEvent()
    * 
    * 
    * 
    * 重点关注:
    * 1 子View对于ACTION_DOWN的处理十分重要!!!!!
    *   ACTION_DOWN是一系列Touch事件的开端,如果子View对于该ACTION_DOWN事件在onTouchEvent()中返回了false即未消费.
    *   那么ViewGroup就不会把后续的ACTION_MOVE和ACTION_UP派发给该子View.在这种情况下ViewGroup就和普通的View一样了,
    *   调用该ViewGroup自己的dispatchTouchEvent()从而调用自己的onTouchEvent();即不会将事件分发给子View.
    *   详细代码请参见如下代码分析.
    *   
    * 2 为什么子view对于Touch事件处理返回true那么其上层的ViewGroup就无法处理Touch事件了?????
    *   这个想必大家都知道了,因为该Touch事件被子View消费了其上层的ViewGroup就无法处理该Touch事件了.
    *   那么在源码中的依据是什么呢??请看下面的源码分析
    *   
    * 参考资料:
    * 0 http://blog.csdn.net/sahadev_/article/details/23839039
    * 1 http://www.cnblogs.com/xiaoweiz/p/3838682.html
    * 2 http://hukai.me/android-deeper-touch-event-dispatch-process/
    * 3 http://blog.csdn.net/hdxiaoyu2/article/details/25563453
    * 4 http://www.eoeandroid.com/thread-542296-1-1.html
    * 5 http://www.eoeandroid.com/forum.php?mod=viewthread&tid=319301
    * 6 http://stackvoid.com/details-dispatch-onTouch-Event-in-Android/
    *   Thank you very much
    */
    

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

       if (mInputEventConsistencyVerifier != null) {
           mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
       }
    
       boolean handled = false;
       if (onFilterTouchEventForSecurity(ev)) {
           final int action = ev.getAction();
           final int actionMasked = action & MotionEvent.ACTION_MASK;
    
           /**
            * 第一步:对于ACTION_DOWN进行处理(Handle an initial down)
            * 因为ACTION_DOWN是一系列事件的开端,当是ACTION_DOWN时进行一些初始化操作.
            * 从源码的注释也可以看出来:清除以往的Touch状态(state)开始新的手势(gesture)
            * cancelAndClearTouchTargets(ev)中有一个非常重要的操作:
            * 将mFirstTouchTarget设置为null!!!!
            * 随后在resetTouchState()中重置Touch状态标识
            */
           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.
               cancelAndClearTouchTargets(ev);
               resetTouchState();
           }
    
            /**
             * 第二步:检查是否要拦截(Check for interception)
             * 在dispatchTouchEvent(MotionEventev)这段代码中
             * 使用变量intercepted来标记ViewGroup是否拦截Touch事件的传递.
             * 该变量在后续代码中起着很重要的作用.
             */
            final boolean intercepted;
            // 事件为ACTION_DOWN或者mFirstTouchTarget不为null(即已经找到能够接收touch事件的目标组件)时if成立
            if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
                //判断disallowIntercept(禁止拦截)标志位
                //因为在其他地方可能调用了requestDisallowInterceptTouchEvent(boolean disallowIntercept)
                //从而禁止执行是否需要拦截的判断(有点拗口~其实看requestDisallowInterceptTouchEvent()方法名就可明白)
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                //当没有禁止拦截判断时(即disallowIntercept为false)调用onInterceptTouchEvent(ev)方法
                if (!disallowIntercept) {
                    //既然disallowIntercept为false那么就调用onInterceptTouchEvent()方法将结果赋值给intercepted
                    //常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent
                    //其实在这就是一个体现,在dispatchTouchEvent()中调用了onInterceptTouchEvent()
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                     //当禁止拦截判断时(即disallowIntercept为true)设置intercepted = false
                    intercepted = false;
                }
            } else {
                //当事件不是ACTION_DOWN并且mFirstTouchTarget为null(即没有Touch的目标组件)时
                //设置 intercepted = true表示ViewGroup执行Touch事件拦截的操作。
                //There are no touch targets and this action is not an initial down
                //so this view group continues to intercept touches.
                intercepted = true;
            }


            /**
             * 第三步:检查cancel(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;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            //不是ACTION_CANCEL并且ViewGroup的拦截标志位intercepted为false(不拦截)
            if (!canceled && !intercepted) {
                //处理ACTION_DOWN事件.这个环节比较繁琐.
                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 (childrenCount != 0) {
                        // 依据Touch坐标寻找子View来接收Touch事件
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final View[] children = mChildren;
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);

                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                        // 遍历子View判断哪个子View接受Touch事件
                        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) {
                                // 找到接收Touch事件的子View!!!!!!!即为newTouchTarget.
                                // 既然已经找到了,所以执行break跳出for循环
                                // 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不满足,当然也不会执行break语句.
                             * 于是代码会执行到这里来.
                             * 
                             * 调用方法dispatchTransformedTouchEvent()将Touch事件传递给子View做
                             * 递归处理(也就是遍历该子View的View树)
                             * 该方法很重要,看一下源码中关于该方法的描述:
                             * 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.
                             * 将Touch事件传递给特定的子View.
                             * 该方法十分重要!!!!在该方法中为一个递归调用,会递归调用dispatchTouchEvent()方法!!!!!!!!!!!!!!
                             * 在dispatchTouchEvent()中:
                             * 如果子View为ViewGroup并且Touch没有被拦截那么递归调用dispatchTouchEvent()
                             * 如果子View为View那么就会调用其onTouchEvent(),这个就不再赘述了.
                             * 
                             * 
                             * 该方法返回true则表示子View消费掉该事件,同时进入该if判断.
                             * 满足if语句后重要的操作有:
                             * 1 给newTouchTarget赋值
                             * 2 给alreadyDispatchedToNewTouchTarget赋值为true.
                             *   看这个比较长的英语名字也可知其含义:已经将Touch派发给新的TouchTarget
                             * 3 执行break.
                             *   因为该for循环遍历子View判断哪个子View接受Touch事件,既然已经找到了
                             *   那么就跳出该for循环.
                             * 4 注意:
                             *   如果dispatchTransformedTouchEvent()返回false即子View
                             *   的onTouchEvent返回false(即Touch事件未被消费)那么就不满足该if条件,也就无法执行addTouchTarget()
                             *   从而导致mFirstTouchTarget为null.那么该子View就无法继续处理ACTION_MOVE事件
                             *   和ACTION_UP事件!!!!!!!!!!!!!!!!!!!!!!
                             * 5 注意:
                             *   如果dispatchTransformedTouchEvent()返回true即子View
                             *   的onTouchEvent返回true(即Touch事件被消费)那么就满足该if条件.
                             *   从而mFirstTouchTarget不为null!!!!!!!!!!!!!!!!!!!
                             * 6 小结:
                             *   对于此处ACTION_DOWN的处理具体体现在dispatchTransformedTouchEvent()
                             *   该方法返回boolean,如下:
                             *   true---->事件被消费----->mFirstTouchTarget!=null
                             *   false--->事件未被消费---->mFirstTouchTarget==null
                             *   因为在dispatchTransformedTouchEvent()会调用递归调用dispatchTouchEvent()和onTouchEvent()
                             *   所以dispatchTransformedTouchEvent()的返回值实际上是由onTouchEvent()决定的.
                             *   简单地说onTouchEvent()是否消费了Touch事件(true or false)的返回值决定了dispatchTransformedTouchEvent()
                             *   的返回值!!!!!!!!!!!!!从而决定了mFirstTouchTarget是否为null!!!!!!!!!!!!!!!!从而进一步决定了ViewGroup是否
                             *   处理Touch事件.这一点在下面的代码中很有体现.
                             *   
                             * 
                             */
                            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条件表示:
                     * 经过前面的for循环没有找到子View接收Touch事件并且之前的mFirstTouchTarget不为空
                     */
                    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指向了最初的TouchTarget
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }



            /**
             * 分发Touch事件至target(Dispatch to touch targets)
             * 
             * 经过上面对于ACTION_DOWN的处理后mFirstTouchTarget有两种情况:
             * 1 mFirstTouchTarget为null
             * 2 mFirstTouchTarget不为null
             * 
             * 当然如果不是ACTION_DOWN就不会经过上面较繁琐的流程
             * 而是从此处开始执行,比如ACTION_MOVE和ACTION_UP
             */
            if (mFirstTouchTarget == null) {
                /**
                 * 情况1:mFirstTouchTarget为null
                 * 
                 * 经过上面的分析mFirstTouchTarget为null就是说Touch事件未被消费.
                 * 即没有找到能够消费touch事件的子组件或Touch事件被拦截了,
                 * 则调用ViewGroup的dispatchTransformedTouchEvent()方法处理Touch事件则和普通View一样.
                 * 即子View没有消费Touch事件,那么子View的上层ViewGroup才会调用其onTouchEvent()处理Touch事件.
                 * 在源码中的注释为:No touch targets so treat this as an ordinary view.
                 * 也就是说此时ViewGroup像一个普通的View那样调用dispatchTouchEvent(),且在dispatchTouchEvent()
                 * 中会去调用onTouchEvent()方法.
                 * 具体的说就是在调用dispatchTransformedTouchEvent()时第三个参数为null.
                 * 第三个参数View child为null会做什么样的处理呢?
                 * 请参见下面dispatchTransformedTouchEvent()的源码分析
                 * 
                 * 这就是为什么子view对于Touch事件处理返回true那么其上层的ViewGroup就无法处理Touch事件了!!!!!!!!!!
                 * 这就是为什么子view对于Touch事件处理返回false那么其上层的ViewGroup才可以处理Touch事件!!!!!!!!!!
                 * 
                 */
                handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
            } else {
                /**
                 * 情况2:mFirstTouchTarget不为null即找到了可以消费Touch事件的子View且后续Touch事件可以传递到该子View
                 * 在源码中的注释为:
                 * 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;
                        //对于非ACTION_DOWN事件继续传递给目标子组件进行处理,依然是递归调用dispatchTransformedTouchEvent()
                        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;
                }
            }

            /**
             * 处理ACTION_UP和ACTION_CANCEL
             * Update list of touch targets for pointer up or cancel, if needed.
             * 在此主要的操作是还原状态
             */
            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;
    }



    //=====================以上为dispatchTouchEvent()源码分析======================



    //===============以下为dispatchTransformedTouchEvent()源码分析=================

    /**
     * 在dispatchTouchEvent()中调用dispatchTransformedTouchEvent()将事件分发给子View处理
     * 
     * 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.
     * 
     * 在此请着重注意第三个参数:View child
     * 在dispatchTouchEvent()中多次调用了dispatchTransformedTouchEvent(),但是有时候第三个参数为null,有时又不是.
     * 那么这个参数是否为null有什么区别呢?
     * 在如下dispatchTransformedTouchEvent()源码中可见多次对于child是否为null的判断,并且均做出如下类似的操作:
     * if (child == null) {
     *       handled = super.dispatchTouchEvent(event);
     *    } else {
     *       handled = child.dispatchTouchEvent(event);
     * }
     * 这个代码是什么意思呢??
     * 当child == null时会将Touch事件传递给该ViewGroup自身的dispatchTouchEvent()处理.
     * 即super.dispatchTouchEvent(event)正如源码中的注释描述的一样:
     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
     * 当child != null时会调用该子view(当然该view可能是一个View也可能是一个ViewGroup)的dispatchTouchEvent(event)处理.
     * 即child.dispatchTouchEvent(event);
     * 
     * 
     */
    private boolean dispatchTransformedTouchEvent(MotionEvent event,boolean cancel,View child,int desiredPointerIdBits) {
        final boolean handled;

        // Canceling motions is a special case.  We don't need to perform any transformations
        // or filtering.  The important part is the action, not the contents.
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // Calculate the number of pointers to deliver.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        // If for some reason we ended up in an inconsistent state where it looks like we
        // might produce a motion event with no pointers in it, then drop the event.
        if (newPointerIdBits == 0) {
            return false;
        }

        // If the number of pointers is the same and we don't need to perform any fancy
        // irreversible transformations, then we can reuse the motion event for this
        // dispatch as long as we are careful to revert any changes we make.
        // Otherwise we need to make a copy.
        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);
        }

        // Perform any necessary transformations and dispatch.
        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;
    }







}

  • 上一篇Android事件分发详细明白(四卡塔尔——事件传递幼功示例
  • 下一篇Android事件分发详细明白(二卡塔尔——Touch事件传入到Activity的流程


1


0

onInterceptTouchEvent调用条件

final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    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;
}

public boolean dispatchTouchEvent(MotionEvent ev)

View/ViewGroup处监护人件分发的发起者,View/ViewGroup选用到触控事件首先调起的正是这一个方法,然后在该办法中推断是不是管理拦截或是将事件分发给子容器

主导难点

日子分发机制的困难在哪,笔者以为难的地点以下几点:几个措施调用法则,明显处管事人件的指标以至事件冲突的缓慢解决方式。

多少个基本点办法

事件传递准则

诚如二遍点击会有一多级的Motion伊夫nt,能够省略分为:down->move->….->move->up,当三遍event分发到ViewGroup时,上述多个办法之间的
ViewGroup中调用顺序可以用一段轻易代码表示

MotionEvent ev;//down or move or up or others...
viewgroup.dispatchTouchEvent(ev);

public boolean dispatchTouchEvent(MotionEvent ev){
 boolean isConsumed = false;
   if(onInterceptTouchEvent(ev)){
     isCousumed = this.onTouchEvent(ev);
   }else{
      isConsumed = childView.dispatchTouchEvent(ev);
   }
   return isConsumed;
}

归来结果true表示事件被处理了,重返false表示从没管理。上边的代码老妪能解,看起来也很简单,一句话就会总结,ViewGroup收到事件后调用dispatch,在dispatch中先检查是或不是要堵住,若拦截则ViewGroup吃掉事件,不然交给有管理技巧的子容器管理。

可是,轻便归轻巧,写成那样只是为着便利精通,ViewGroup的事件管理流程自然没这么轻巧,这里忽视了无数细节难题,接下去继续补充。回到位置说的,一文山会海事件我们常常管理的相符都是一个down,七个move和三个up,光靠上边的伪代码是不可能把这个主题素材都给完美消除,直接来看ViewGroup的dispatchTouch伊芙nt。

总结那一个触控的风浪分发机制正是弄领会多个办法,dispatchTouchEvent(卡塔尔国,OnInterceptTouch伊芙nt(State of Qatar,onTouchEvent(卡塔尔,和那多少个主意与n个ViewGroup和View堆集在同步的标题,再复杂的组织都能拆分成1个ViewGroup+1个View。

儿女是什么人的

持续来扩大大家的伪代码,拦截条件决断完之后,决定把事件后续传递给子View的时候,会调用childView.dispatchTouch伊芙nt(ev卡塔尔,难题来了,child是哪来的,继续看源码

if (!canViewReceivePointerEvents(child)
   || !isTransformedTouchPointInView(x, y, child, null)) {
     ev.setTargetAccessibilityFocus(false);
     continue;
}

ViewGroup通过判别全体的子View是还是不是可以知道是不是在播放动画和是不是在点击范围内来决定它是不是能够有身份选用事件。唯有满足条件的child才干够调用dispatch。

再看伪代码,最终dispatch再次回到ViewGroup的isConsumed,若isConsume ==

实在ViewGroup和View都是相差无几,View只是未有了子容器,自然海市蜃楼拦截难题,dispatch也不会细小略,所以弄通晓了ViewGroup其实就懂的基本上了。

解释一下下边包车型地铁代码,看起来好像异常粗略,但的确很简单吗。。在解释从前先说一下intercepted代表的意思,intercepted

false表示父容器ViewGroup临时不阻拦事件,事件有机会传给子View管理,再次回到true表示父容器直接堵住了该连串事件,后续不会再传递给子View了。子View想获取事件只可以让该值为false

onInterceptTouch伊芙nt调用重回false(重临false技术传递给子View,对应到上边伪代码的else中的内容,叫事件传递到子容器须要满足的开始和结果更加好精晓一些)需求满足七个条件中的任性一个就有望接触(当然只是有希望):

多少个是在down的时候,另三个正是mFirstTouchTarget!=null,那mFirstTouchTarget几时不为空,有意思味的校友能够看ViewGroup中的addTouchTarget这么些情势的调用机遇,mFirstTouchTarget正是在这里间赋值的,源码太长作者就不贴了。

mFirstTouchTarget是用来保存ViewGroup中花费了ACTION_DOWN事件的子View,即在上边伪代码中child.dispatchTouch伊夫nt(evState of Qatar在ACTION_DOWN的时候回来true的View,只要有子View的dispatch在ACTION_DOWN再次回到true,就不会为null(这一个赋值进程只暴发在ACTION_DOWN里,如果子ViewACTION__DOWN不给它赋值后面连串的事件就不会再),反之,若无子View处理,该目的即为null。当然,知足了上述四个规格还不行,必须还要满意!disallowIntercept。

disallowIntercept这几个变量很风趣,它的值首要受FLAG_DISALLOW_INTERCEPT这么些标识影响,那些值能够被ViewGroup的子View设置,ViewGroup的子View假使调用了requestDisallowInterceptTouch伊芙nt那一个方式,会退换FLAG_DISALLOW_INTERCEPT,导致disallowIntercept这几个值正是ture了,这种状态会跳过intercept,导致拦截失效。

但那事还未了,FLAG_DISALLOW_INTERCEPT这些标识有贰个重新恢复生机设置的编制,查看ViewGroup源码能够看看,在拍卖MotionEvent.ACTION_DOWN的时候会重新初始化那么些符号招致disallowIntercept失效,是或不是严酷,上面的一段这么简单的代码有与上述同类多幺蛾子,这里还可以得到二个结论,ACTION_DOWN的时候势必能够执行onInterceptTouch伊芙nt的。

为此拦截的intercepted很要紧,能影响到底是让ViewGroup还子View管理这几个事件。

下边包车型客车五个有希望接触拦截的条件说罢了,那么当三个规格都不知足的话就不会再调用拦截了(拦截很关键,平时ViewGroup都回到false那样能把事件传递给子View,假若在ACTION_DOWN时不能够走到OnInterceptTouchEvent并回到false告诉ViewGroup不要拦截,则事件再也不可能传给子View了,所以拦截常常都以要走到的,并且貌似都以回到false这样能让子View有空子处理),这种情景相仿都以在ACTION_DOWN管理完现在未有子View当接盘侠开销ACTION_DOWN以至后续事件,从上边的伪代码可以看出来,那时ViewGroup自身就很被动了,要求和睦来调用onTouchEvent来拍卖,那锅就和好背了。

再持续说一下mFirstTouchTarget和intercepted是怎么影响事件方向的。看源码:

if (!canceled && !intercepted) {
....
if (actionMasked == MotionEvent.ACTION_DOWN
                       || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                       || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
        ....
        for(child : childList){
            if(!child satisfied condition....){
                continue;
            }
            newTouchTarget = addTouchTarget(child, idBitsToAssign);//在这里给mFirstTouchTarget赋值
        }

        }
}

能够在那间见到intercepted为false在ACTION_DOWN里工夫给地点说过的mFirstTouchTarget赋值,独有mFirstTouchTarget不为空手艺让持续事件传递给子View,不然依照上地点说的代码后续事件只好给父容器管理了。

mFirstTouchTarget正是我们继续事件传递的靶子,相当轻松通晓,如若在ACTION_DOWN中从未显然这么些指标,则延续事件不理解传递给什么人自然就付给父容器ViewGroup管理了,真正处总管件传递的形式是dispatchTransformedTouch伊芙nt,再看源码:

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        // Canceling motions is a special case.  We don't need to perform any transformations
        // or filtering.  The important part is the action, not the contents.
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

}

见状没,只要参数里传的child为空,则ViewGroup调用super.dispatchTouchEvent(event卡塔尔,super是哪个人,ViewGroup世襲自View,当然是View咯,View的dispatch调用的哪个人?当然是投机的onTouchEvent(前面会说),所以这么些最后照旧调用了ViewGroup本身的onTouch伊夫nt。

那正是说当child!=null的时候啊,调用的是child的dispatchTouch伊芙nt(event卡塔尔,即便child恐怕是View也说不佳是ViewGroup,假若是ViewGroup则继续信守上面的伪代码实行事件分发,如果也是View则调用本身的onTouchEvent。

所以,提起底事件究竟给何人管理,依然和传进来的child有关,那那么些办法在哪里调用的吗,继续看:

if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
            ...
            dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)
        }

那就是为啥mFirstTouchTarget能影响事件分发的大方向的案由。就那样,整个伪代码的流水生产线是还是不是很清楚了。

那边要求多说两句,在上地点代码流程中,intercepted决定了那几个事件会不会调用ViewGroup的onTouch伊芙nt,当intercepted为true则后续流程会调用ViewGroup的onTouchEvent,留神看下面的代码能窥见,独有二种情状为ture:一是调用了InterceptTouch伊夫nt把事件拦截下来,另叁个固然未有三个子View能够花费ActionDown。只有那三种状态父容器ViewGroup才会协和解和管理理
那么难题来了,思考二个难题:假设实View管理了ACTION_DOWN但后续事件都回来false,那么些从未被拍卖的风云结尾传给哪个人管理了?各位思索之,前边再说那些主题素材。

Android开荒,触控无处不在。对于某些不咋看源码的同班来说,多少对那块都会有点吸引。View事件的分发机制,不唯有在做职业须要中会遇到这几个标题,在一部分面试笔试题中也常常有人问,可谓是不合时宜了。笔者从前也看过不菲人写的那上边包车型地铁篇章,不是说的太啰嗦正是太模糊,还也许有局地在细节上写的也可以有争辩,故再次重新收拾一下那块内容,十分钟让您搞理解View事件的分发机制。

public boolean onInterceptTouchEvent(MotionEvent ev)

ViewGroup专项使用,通过该方法能够到达控件事件的分发方向,平日能够在该措施中判别将事件给ViewGroup独吞或是它连续传递给子容器,是处管事人件冲突的最棒地点

public boolean onTouchEvent(MotionEvent event)

触控事件的着实管理者,最终每一种事件都会在这里间被管理

发表评论

电子邮件地址不会被公开。 必填项已用*标注