美文网首页Android 进阶之旅
Android 进阶学习(五) Android 事件传递View

Android 进阶学习(五) Android 事件传递View

作者: Tsm_2020 | 来源:发表于2020-11-11 13:43 被阅读0次

    上一篇文章我们总结了事件在Activity 和View之间的传递,我们知道还有一种特殊的情况那就是需要加上ViewGroup, 这样才是一个完整的事件传递过程,因为View 就是绘制过程中最小的粒子,不能包含其他view ,而ViewGroup 是可以包含其他View 的,
    我们将上一篇的例子在加上一个RelativeLayout ,我们贴一下代码

    public class TsmRelativeLayout extends RelativeLayout {
       public TsmRelativeLayout(Context context) {
           super(context);
       }
    
       public TsmRelativeLayout(Context context, AttributeSet attrs) {
           super(context, attrs);
       }
    
       public TsmRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
           super(context, attrs, defStyleAttr);
       }
    
       @Override
       public boolean dispatchTouchEvent(MotionEvent event) {
           log    ("TsmRelativeLayout            dispatchTouchEvent"+"      " + "action"+event.getAction());
    //        if(event.getAction()==MotionEvent.ACTION_DOWN){
    //            return true;
    //        }
           return super.dispatchTouchEvent(event);
       }
       @Override
       public boolean onInterceptTouchEvent(MotionEvent event) {
           log    ("TsmRelativeLayout            onInterceptTouchEvent"+"      " + "action"+event.getAction());
           return false;
       }
    
       @Override
       public boolean onTouchEvent(MotionEvent event) {
           log    ("TsmRelativeLayout            onTouchEvent"+"      " + "action"+event.getAction());
           return super.onTouchEvent(event);
       }
       
       
       public void log(String tag){
           LogUtils.i(tag);
       }
       
    }
    
    public class TsmTextView extends androidx.appcompat.widget.AppCompatTextView {
       public TsmTextView(Context context) {
           super(context);
       }
    
       public TsmTextView(Context context, @Nullable AttributeSet attrs) {
           super(context, attrs);
       }
    
       public TsmTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
           super(context, attrs, defStyleAttr);
       }
    
       @Override
       public boolean dispatchTouchEvent(MotionEvent event) {
           log    ("TsmTextView            dispatchTouchEvent"+"      " + "action"+event.getAction());
           return super.dispatchTouchEvent(event);
       }
    
       @Override
       public boolean onTouchEvent(MotionEvent event) {
           log    ("TsmTextView            onTouchEvent"+"      " + "action"+event.getAction());
           return super.onTouchEvent(event);
       }
    
       public void log(String tag){
           LogUtils.i(tag);
       }
    }
    public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
    
           TsmTextView tv_view= findViewById(R.id.tv_view);
    
           tv_view.setOnClickListener(this);
    //        tv_view.setOnTouchListener(this);
       }
    
    
       @Override
       public boolean dispatchTouchEvent(MotionEvent ev) {
           LogUtils.i("MainActivity   dispatchTouchEvent    action:"+ev.getAction());
           return super.dispatchTouchEvent(ev);
       }
    
    
       @Override
       public boolean onTouchEvent(MotionEvent event) {
           LogUtils.i("MainActivity   onTouchEvent    action:"+event.getAction());
           return super.onTouchEvent(event);
       }
    
       @Override
       public void onClick(View v) {
           LogUtils.i("onClick");
       }
    
       @Override
       public boolean onTouch(View v, MotionEvent event) {
           switch (v.getId()){
               case R.id.tv_view:
                   LogUtils.i("MainActivity       onTouch"+"        action:"+ event.getAction());
           }
           return false;
       }
    }
    
    

    打印结果

    msg:==>MainActivity   dispatchTouchEvent    action:0
    msg:==>TsmRelativeLayout            dispatchTouchEvent      action0
    msg:==>TsmRelativeLayout            onInterceptTouchEvent      action0
    msg:==>TsmTextView            dispatchTouchEvent      action0
    msg:==>MainActivity       onTouch        action:0
    msg:==>TsmTextView            onTouchEvent      action0
    msg:==>MainActivity   dispatchTouchEvent    action:2
    msg:==>TsmRelativeLayout            dispatchTouchEvent      action2
    msg:==>TsmRelativeLayout            onInterceptTouchEvent      action2
    msg:==>TsmTextView            dispatchTouchEvent      action2
    msg:==>MainActivity       onTouch        action:2
    msg:==>TsmTextView            onTouchEvent      action2
    msg:==>MainActivity   dispatchTouchEvent    action:2
    msg:==>TsmRelativeLayout            dispatchTouchEvent      action2
    msg:==>TsmRelativeLayout            onInterceptTouchEvent      action2
    msg:==>TsmTextView            dispatchTouchEvent      action2
    msg:==>MainActivity       onTouch        action:2
    msg:==>TsmTextView            onTouchEvent      action2
    msg:==>MainActivity   dispatchTouchEvent    action:1
    msg:==>TsmRelativeLayout            dispatchTouchEvent      action1
    msg:==>TsmRelativeLayout            onInterceptTouchEvent      action1
    msg:==>TsmTextView            dispatchTouchEvent      action1
    msg:==>MainActivity       onTouch        action:1
    msg:==>TsmTextView            onTouchEvent      action1
    msg:==>onClick
    

    我们发现增加了RelativeLayout后 多了一个方法,那就是onInterceptTouchEvent ,而整个过程就是Activity.dispatchTouchEvent --> RelativeLayout.dispatchTouchEvent -->RelativeLayout.onInterceptTouchEvent -->View.dispatchTouchEvent -->View.onTouch -->View.onTouchEvent 处理完按下事件后又循环处理其他事件,最后执行了onClick方法,消费完事件

    增加了RelativeLayout后和View 事件的派发有相似的地方也有不同的地方,到底有什么不同我们就从ViewGroup 的dispatchTouchEvent 源码来看

    @Override
       public boolean dispatchTouchEvent(MotionEvent ev) {
           if (mInputEventConsistencyVerifier != null) {
               mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
           }
    
           // If the event targets the accessibility focused view and this is it, start
           // normal event dispatch. Maybe a descendant is what will handle the click.
           if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
               ev.setTargetAccessibilityFocus(false);
           }
    
           boolean handled = false;
           if (onFilterTouchEventForSecurity(ev)) {
               final int action = ev.getAction();
               final int actionMasked = action & MotionEvent.ACTION_MASK;
    
               // Handle an initial down.
               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.
               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;
               }
    
               // If intercepted, start normal event dispatch. Also if there is already
               // a view that is handling the gesture, do normal event dispatch.
               if (intercepted || mFirstTouchTarget != null) {
                   ev.setTargetAccessibilityFocus(false);
               }
    
               // 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;
               if (!canceled && !intercepted) {
    
                   // If the event is targeting accessibility focus we give it to the
                   // view that has accessibility focus and if it does not handle it
                   // we clear the flag and dispatch the event to all children as usual.
                   // We are looking up the accessibility focused host to avoid keeping
                   // state since these events are very rare.
                   View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                           ? findChildWithAccessibilityFocus() : null;
    
                   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 ArrayList<View> preorderedList = buildTouchDispatchChildList();
                           final boolean customOrder = preorderedList == null
                                   && isChildrenDrawingOrderEnabled();
                           final View[] children = mChildren;
                           for (int i = childrenCount - 1; i >= 0; i--) {
                               final int childIndex = getAndVerifyPreorderedIndex(
                                       childrenCount, i, customOrder);
                               final View child = getAndVerifyPreorderedView(
                                       preorderedList, children, 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;
                               }
    
                               if (!child.canReceivePointerEvents()
                                       || !isTransformedTouchPointInView(x, y, child, null)) {
                                   ev.setTargetAccessibilityFocus(false);
                                   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();
                                   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();
                                   newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                   alreadyDispatchedToNewTouchTarget = true;
                                   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();
                       }
    
                       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.
               if (mFirstTouchTarget == null) {
                   // No touch targets so treat this as an ordinary view.
                   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;
                   }
               }
    
               // 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;
       }
    

    这一段代码相对于View 的dispatchTouchEvent还要复杂,我们就将它拆分一下,慢慢来分析

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

    这段代码从字面上的意思就是在按下状态时清除原有的flag,并重置state,

          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;
               }
    
               // If intercepted, start normal event dispatch. Also if there is already
               // a view that is handling the gesture, do normal event dispatch.
               if (intercepted || mFirstTouchTarget != null) {
                   ev.setTargetAccessibilityFocus(false);
               }
    

    这段代码主要是为了判断是否打断这个事件,继续分发,判断如果打断 intercepted 或者 目标View mFirstTouchTarget 已经找到,那么就执行event.setTargetAccessibilityFocus(false);字面意思就是设置后续event的访问权限,但是具体怎么执行的是native 方法,看不到了,但是从修改onInterceptTouchEvent的返回值我发现如果设置打断后,那么Viewgroup会立即执行自身的onTouchEvent,并且不会执行与该事件相关的后续事件,肯定还有一些别的操作我们就无法猜测了

    这里面还有一个关于disallowIntercept 的判断, ViewGroup 里面包含了一个设置disallowIntercept 的方法,

       @Override
       public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    
           if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
               // We're already in this state, assume our ancestors are too
               return;
           }
    
           if (disallowIntercept) {
               mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
           } else {
               mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
           }
    
           // Pass it up to our parent
           if (mParent != null) {
               mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
           }
       }
    

    在child里面可以设置是否让ViewGroup 拦截该事件,看到过很多下拉刷新View中都包含了该方法,非常实用的一个方法

    接下来又判断 cancel的状态,

               // Check for cancelation.
               final boolean canceled = resetCancelNextUpFlag(this)
                       || actionMasked == MotionEvent.ACTION_CANCEL;
    

    判断完cancel状态就进入了 根据不同的action 来执行不同的动作,

    接下来判断是否是ACTION_DOWN ,在按下的状态必须要找到和这个按下相关的child 也就是 newTouchTarget ,

    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 ArrayList<View> preorderedList = buildTouchDispatchChildList();
                           final boolean customOrder = preorderedList == null  && isChildrenDrawingOrderEnabled();
                           final View[] children = mChildren;
                           for (int i = childrenCount - 1; i >= 0; i--) {
                               final int childIndex = getAndVerifyPreorderedIndex(  childrenCount, i, customOrder);
                               final View child = getAndVerifyPreorderedView( preorderedList, children, 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;
                               }
                               if (!child.canReceivePointerEvents()
                                       || !isTransformedTouchPointInView(x, y, child, null)) {
                                   ev.setTargetAccessibilityFocus(false);
                                   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();
                                   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();
                                   newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                   alreadyDispatchedToNewTouchTarget = true;
                                   break;
                               }
                               ev.setTargetAccessibilityFocus(false);
                           }
                           if (preorderedList != null) preorderedList.clear();
                       }
    

    发现在寻找newTouchTarget会调用方法dispatchTransformedTouchEvent()将Touch事件传递给特定的子View。该方法十分重要,在该方法中为一个递归调用,会递归调用dispatchTouchEvent()方法。在dispatchTouchEvent()中如果子View为ViewGroup并且Touch没有被拦截那么递归调用dispatchTouchEvent(),如果子View为View那么就会调用其onTouchEvent()。dispatchTransformedTouchEvent方法如果返回true则表示子View消费掉该事件,同时进入该if判断。

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

    还发现如果在发现在寻找newTouchTarget的View 过程中如果dispatchTransformedTouchEvent 循环调用的过程中如果发现有子View消费了事件,那么就要给mFirstTouchTarget 赋值

    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
           final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
           target.next = mFirstTouchTarget;
           mFirstTouchTarget = target;
           return target;
       }
    

    在按下状态时,如果找到不到 newTouchTarget 并且有mFirstTouchTarget , 会通过mFirstTouchTarget 找到newTouchTarget

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

    在执行完ACTION_DOWN的判断,后就是又执行了dispatchTransformedTouchEvent 分发事件,如果找到不到子View 消费事件则执行

                  handled = dispatchTransformedTouchEvent(ev, canceled, null,
                           TouchTarget.ALL_POINTER_IDS);
    

    注意上面第三个参数为null,

    如果找到了消费事件的子View 则 执行

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

    就是说找到了可以消费Touch事件的子View且后续Touch事件可以传递到该子View。可以看见在源码的else中对于非ACTION_DOWN事件继续传递给目标子组件进行处理,依然是递归调用dispatchTransformedTouchEvent()方法来实现的处理。

    总结

    ViewGroup 事件的派发是由顶层VeiwGroup 递归调用分发给View的,我们可以使用onInterceptTouchEvent 打断这个分发的过程,onInterceptTouchEvent返回true则表示需要拦截当前这个事件,切该事件的后续事件也不会派发给当前的ViewGroup

    相关文章

      网友评论

        本文标题:Android 进阶学习(五) Android 事件传递View

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