千锋教育-做有情怀、有良心、有品质的职业教育机构

400-811-9990
手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

上海
  • 北京
  • 郑州
  • 武汉
  • 成都
  • 西安
  • 沈阳
  • 广州
  • 南京
  • 深圳
  • 大连
  • 青岛
  • 杭州
  • 重庆
当前位置:西安千锋IT培训  >  技术干货  >  ACTION_CANCEL到底何时触发,滑出子View范围会发生什么?

ACTION_CANCEL到底何时触发,滑出子View范围会发生什么?

来源:千锋教育
发布人:xqq
时间: 2023-10-20 11:22:41

一、ACTION_CANCEL在这些时候会触发

1、父view拦截事件

首先要了解ViewGroup什么情况下会拦截事件,请看下面一段代码:

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) ...    boolean handled = false;    if (onFilterTouchEventForSecurity(ev))         final int action = ev.getAction();        final int actionMasked = action & MotionEvent.ACTION_MASK;...        // 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;                ...        ...

可以看出有两个条件:

MotionEvent.ACTION_DOWN事件或者mFirstTouchTarget非空也就是有子view在处理事件子view没有做拦截,也就是没有调用ViewParent#requestDisallowInterceptTouchEvent(true)

如果满足上面的两个条件才会执行onInterceptTouchEvent(ev)。如果ViewGroup拦截了事件,则intercepted变量为true,接着往下看:

@Overridepublic boolean dispatchTouchEvent(MotionEvent ev)         boolean handled = false;    if (onFilterTouchEventForSecurity(ev))         ...        // Check for interception.        final boolean intercepted;        if (actionMasked == MotionEvent.ACTION_DOWN                || mFirstTouchTarget != null)             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;            if (!disallowIntercept)                 // 当mFirstTouchTarget != null,也就是子view处理了事件                // 此时如果父ViewGroup拦截了事件,intercepted==true                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;                ...        // Dispatch to touch targets.        if (mFirstTouchTarget == null)             ...         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)                     ...                 else                     // 判断一:此时cancelChild == true                    final boolean cancelChild = resetCancelNextUpFlag(target.child)                            || intercepted;// 判断二:给child发送cancel事件                    if (dispatchTransformedTouchEvent(ev, cancelChild,                            target.child, target.pointerIdBits))                         handled = true;                                        ...                ...    return handled;

以上判断一处cancelChild为true,然后进入判断二中一看究竟:

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设置成ACTION_CANCEL        event.setAction(MotionEvent.ACTION_CANCEL);        if (child == null)             ...         else             // 分发给child            handled = child.dispatchTouchEvent(event);                event.setAction(oldAction);        return handled;

当参数cancel为ture时会将event设置为MotionEvent.ACTION_CANCEL,然后分发给child。

2、ACTION_DOWN初始化操作

首先请看一段代码:

public boolean dispatchTouchEvent(MotionEvent ev)     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.            // 取消并清除所有的Touch目标            cancelAndClearTouchTargets(ev);            resetTouchState();        ...        ...

系统可能会由于App切换、ANR等原因丢失了up,cancel事件。因此需要在ACTION_DOWN时丢弃掉所有前面的状态,具体代码如下:

private void cancelAndClearTouchTargets(MotionEvent event)     if (mFirstTouchTarget != null)         boolean syntheticEvent = false;        if (event == null)             final long now = SystemClock.uptimeMillis();            event = MotionEvent.obtain(now, now,                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);            syntheticEvent = true;                for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next)             resetCancelNextUpFlag(target.child);            // 分发事件同情况一            dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);                ...

PS:在dispatchDetachedFromWindow()中也会调用cancelAndClearTouchTargets()

3、在子View处理事件的过程中被从父View中移除时

请看下面这段代码:

public void removeView(View view)     if (removeViewInternal(view))         requestLayout();        invalidate(true);    private boolean removeViewInternal(View view)     final int index = indexOfChild(view);    if (index >= 0)         removeViewInternal(index, view);        return true;        return false;private void removeViewInternal(int index, View view)     ...    cancelTouchTarget(view);...private void cancelTouchTarget(View view)     TouchTarget predecessor = null;    TouchTarget target = mFirstTouchTarget;    while (target != null)         final TouchTarget next = target.next;        if (target.child == view)             ...            // 创建ACTION_CANCEL事件            MotionEvent event = MotionEvent.obtain(now, now,                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);            分发给目标view            view.dispatchTouchEvent(event);            event.recycle();            return;                predecessor = target;        target = next;

4、子View被设置了PFLAG_CANCEL_NEXT_UP_EVENT标记时

请看下面这段代码,在情况一种的两个判断处:

// 判断一:此时cancelChild == truefinal boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;// 判断二:给child发送cancel事件if (dispatchTransformedTouchEvent(ev, cancelChild,    target.child, target.pointerIdBits))     handled = true;

当 resetCancelNextUpFlag(target.child) 为true时同样也会导致cancel,查看代码:

/** * Indicates whether the view is temporarily detached. * * @hide */static final int PFLAG_CANCEL_NEXT_UP_EVENT        = 0x04000000;private static boolean resetCancelNextUpFlag(View view)     if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0)         view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;        return true;        return false;

二、滑出子View范围会发生什么

通常来说,滑出子View范围什么也不会发生。如果手指移出了子View之外,从而导致事件序列被取消,那么通常不会有太多事情发生。您的应用程序将会收到一个ACTION_CANCEL事件,但是由于事件已经被取消,您无法执行任何进一步的操作。如果您希望避免这种情况发生,您可以尝试使用requestDisallowInterceptTouchEvent()方法来防止触摸事件序列被拦截,或者重新设计您的UI以确保用户不会意外地移动手指到View的范围外。

延伸阅读1:ACTION_CANCEL作用

我们知道如果某一个子View处理了Down事件,那么随之而来的Move和Up事件也会交给它处理。但是交给它处理之前,父View还是可以拦截事件的,如果拦截了事件,那么子View就会收到一个Cancel事件,并且不会收到后续的Move和Up事件。

声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。

猜你喜欢LIKE

在mysql中, 为什么只有右模糊才走索引?

2023-10-20

SQL语言中的ALTER和UPDATE,DROP和DELETE都有什么区别?

2023-10-20

一个大型的SNS网站,是否适合数据库全部用mongodb来做,为什么?

2023-10-20

最新文章NEW

为什么声明性语言往往适合于并行执行,命令代码很难在多个内核和多个机器之间并行化?

2023-10-20

MySQL多表关联查询效率高点还是多次单表查询效率高,为什么?

2023-10-20

jmeter性能测试步骤?

2023-10-20

相关推荐HOT

更多>>

快速通道 更多>>

最新开班信息 更多>>

网友热搜 更多>>