ACTION_CANCEL到底何时触发,滑出子View范围会发生什么?
一、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
相关推荐HOT
更多>>
关系型数据库中的字段默认值、不可为空、少数索引约束的优缺点是什么?
一、关系型数据库中的字段默认值、不可为空、少数索引约束的优缺点1.字段默认值:针对每个字段都有自己的默认值,较有利于进行统计和分析,以及...详情>>
2023-10-20 21:56:39
Gradle Transform到底是什么怎么用?
一、Gradle Transform到底是什么Gradle Transform是Android官方提供给开发者在项目构建阶段(.class -> .dex转换期间)用来修改.class文件的一...详情>>
2023-10-20 20:24:09
MyBatis和jOOQ有哪些区别?
一、MyBatis和jOOQ的区别1、数据库操作风格不同MyBatis是一种基于XML或注解配置的SQL映射框架。它通过编写SQL语句,并使用对象映射将结果集映射...详情>>
2023-10-20 19:06:20
ACTION_CANCEL到底何时触发,滑出子View范围会发生什么?
一、ACTION_CANCEL在这些时候会触发1、父view拦截事件首先要了解ViewGroup什么情况下会拦截事件,请看下面一段代码:@Overridepublic boolean d...详情>>
2023-10-20 11:22:41热门推荐
在mysql中, 为什么只有右模糊才走索引?
沸为什么声明性语言往往适合于并行执行,命令代码很难在多个内核和多个机器之间并行化?
热SQL语言中的ALTER和UPDATE,DROP和DELETE都有什么区别?
热关系型数据库中的字段默认值、不可为空、少数索引约束的优缺点是什么?
新MySQL多表关联查询效率高点还是多次单表查询效率高,为什么?
jmeter性能测试步骤?
Gradle Transform到底是什么怎么用?
Excel与数据库有什么不同?
MyBatis和jOOQ有哪些区别?
什么是web前端?
一个大型的SNS网站,是否适合数据库全部用mongodb来做,为什么?
在数据库查询的底层实现上SQL Server和MySQL的区别是什么?
外企银行一般用什么linux版本系统和数据库呢?
neo4j有什么缺点?
技术干货






