美文网首页android事件分发
二.ViewGroup事件分发源码分析之事件处理

二.ViewGroup事件分发源码分析之事件处理

作者: 真胖大海 | 来源:发表于2017-10-12 10:19 被阅读21次

    关键源码(对dispatchTouchEvent()的重要部分)

    片段一 是否拦截事件

     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;
        }
    

    片段二 如果ViewGroup不拦截事件

    
    if (!canceled && !intercepted) {
     for(遍历child){
      if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
            newTouchTarget = addTouchTarget(child, idBitsToAssign);
            break;
      }
     }
    }
    
    

    片段三 有无Child处理事件

    if (mFirstTouchTarget == null) {
      // No touch targets so treat this as an ordinary view.
              handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
    }else{
    // 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;//拦截时,cancelChild为true
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                    target.child, target.pointerIdBits)) {
                                handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                   mFirstTouchTarget = next;//会将mFirsrtTouchTarget置空
                            } else {
                                    predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                            }
                    }
                        predecessor = target;
                        target = next;
               }
               
               if (canceled
                        || actionMasked == MotionEvent.ACTION_UP
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    resetTouchState();
                } 
                
            }
        }
        
    

    disallowIntercept 默认值为false,通过viewGroup.requestDisallowInterceptTouchEvent(boolean)进行设置。本文只讨论disallowIntercept 为false的情况。
    mFirstTouchTarget==null 没有child处理事件,有两种可能

    1. ViewGroup拦截了事件
    2. ViewGroup没有拦截了事件,但是没有child处理事件

    mFirstTouchTarget!=null 有child处理事件

    1. ViewGrop没有拦截事件,且有child消费了事件

    intercepted表示是否拦截事件,值为true表示拦截事件。

    对ACTION_DOWN事件的处理

    1. 由onInterceptTouchEvent()判断是否拦截(片段一)
    2. 如果ViewGroup拦截事件,则遍历child,将事件交给child处理,如果有child消耗了事件,则调用addTouchTarget()给mFirstTouchTarget赋值。(片段二)
    3. 如果child不消费ACTION_DWON(mFirstTouchTarget==null),则ViewGroup处理该ACTION_DOWN事件。(片段三)

    对ACTION_MOVE事件的处理

    1. 如果有child消费正在处理事件( mFirstTouchTarget != null),则由onInterceptTouchEvent()判断是否拦截,否则拦截事件(片段一)
    2. 如果ViewGroup拦截ACTION_MOVE事件,则遍历child,将事件交给child处理,如果有child消费了事件,则调用addTouchTarget()给mFirstTouchTarget赋值。(片段二)
    3. 如果没有child消费正在处理事件(mFirstTouchTarget==null),则ViewGroup处理该ACTION_MOVE事件,否则 如果ViewGroup拦截了ACTION_MOVE,则mFirstTouchTarget设置为空。(片段三)

    对ACTOIN_UP事件的处理

    1. 如果有child正在处理事件( mFirstTouchTarget != null),则由onInterceptTouchEvent()判断是否拦截,否则拦截事件(片段一)
    2. 如果ViewGroup拦截ACTION_UP事件,则遍历child,将事件交给child处理,如果有child消耗了事件,则调用addTouchTarget()给mFirstTouchTarget赋值。(片段二)
    3. 如果没有child正在消费事件,则ViewGroup处理该ACTION_MOVE事件,
      否则
      1. 如果ViewGroup拦截了ACTION_UP,则mFirstTouchTarget设置为空。
      2. 重置TouchState状态

    总结

    ACTION_DWON作为起始动作,所以要判断是否拦截

    ACTION_MOVE,ACTION_UP作为后续事件
    如果child在处理事件,ViewGroup要判断是否拦截事件;
    如果没有child在处理事件,ViewGroup自然拦截事件。

    如果ViewGroup不拦截事件,事件交给child处理。

    如果没有Child正在处理事件,则事件交给ViewGroup处理;
    如果有Child正在处理事件,此时如果ViewGroup拦截了当前的事件,则要将ViewGroup标志位正在处理事件即mFirstTouchTarget设置为空。

    相关文章

      网友评论

        本文标题:二.ViewGroup事件分发源码分析之事件处理

        本文链接:https://www.haomeiwen.com/subject/gesnyxtx.html