Android 进阶之旅

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

2020-11-11  本文已影响0人  Tsm_2020

上一篇文章我们总结了事件在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

上一篇 下一篇

猜你喜欢

热点阅读