hi,粉丝朋友!
大家对于MotionEvent.ACTION_CANCEL这个cancel事件是不是感觉又熟悉又陌生,熟悉是因为经常在onTouch识别触摸事件时候会把它和ACTION_UP放在一块处理,基本停留在字面意思理解为 “”取消“”
新课程优惠获取请加入qq群:422901085
Android手机大厂Framework系统-Input系统专题实战课

[入门课,实战课,跨进程专题
ps需要学习深入framework课程和课程优惠

ACTION_CANCEL触发场景和原因:

customTextView.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {Log.i("test2"," onTouch motionEvent = " + motionEvent);if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL || motionEvent.getAction() == MotionEvent.ACTION_UP ) {Log.i("test2"," onTouch ACTION_CANCEL = " ,new Exception());}return true;}
});

所以这里本节课就来带大家深入理解这里ACTION_CANCEL

首先我们来分析应用触摸最常见的ACTION_CANCEL情况:

这个就是我们常见的一个父布局MyLayout,它装载了2个子控件textview1和textview2.假设一个触摸事件到来,我们知道触摸事件的传递顺序就是:父布局 – 》 子布局
这样的一个顺序,也就是其实事件父布局是具有完全的决策权利来是否给子布局,可以通过方法onInterceptTouchEvent。
接下来我们又来看看MotionEvent,一个正常完整触摸应该是怎么个顺序呢?
ACTION_DOWN – > ACTION_MOVE --> ACTION_UP
所以说我们控件正常事件处理就是最少要有DOWN --》UP两个事件,才代表事件介绍,那么你会问如果只有DOWN,和MOVE是否可以呢?答案:当然是不可以的
为啥呢?
大家可以想一下这样一个场景,你是一个按钮,来了触摸事件DOWN了后,你把按钮变成selected状态了,你本来一直等值来个UP事件来变回正常状态,如果系统发生依次你没有收到UP会怎么样呢?那就是你的应用按钮就永远处于选中状态无法取消,一直到进程关闭(当然说是正常你没有特别处理情况)。

所以有了以上基础后,大家就知道有了DOWN事件一般都要有UP事件才算完整,但是有一些场景他就是可能收到了DOWN,后面收不到UP了怎么办?
比如看如下例子:

    @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.i("test2","onInterceptTouchEvent ev = " + ev);if (ev.getAction() == MotionEvent.ACTION_MOVE ) {//我们不拦截DOWN事件,但是拦截MOVE事件,父布局一拦截,子控件就会收到ACTION_CANCELreturn true;}return super.onInterceptTouchEvent(ev);}

这种情况我们父布局只并没有拦截DOWN事件,所以DOWN传递给你textview1,但是因为在MOVE时候我们父布局拦截了,即说明事件不再传递给子控件了textview1,那么这个是不是就是textview1收到了DOWN,但是收不到UP情况,不完整了,那该怎么办?

这个时候其实就是今天重点介绍的ACTION_CANCEL出厂,他就是来帮忙解决上面的因为父布局中间进行了触摸事件拦截,但是子布局又要一个完整触摸过程,那么就需要传递一个触摸事件给子控件,那么传递什么合适?
大家肯定会想UP最合适,UP不就可以了?但是你要想想用户还没有UP啊?如果传递了UP是不是也可能会有问题,所以这时候就是传递ACTION_CANCEL,它代表是取消,即代表这个触摸事件到此被取消结束了。控件收到需要自己做对应的扫尾工作,保证事件完整。

上面我们已经对ACTION_CANCEL触发场景和原因已经清楚,接下来看看源码是怎么处理的:

ACTION_CANCEL出现源码分析

app代码:
layout.xml

     android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:paddingTop="100dp"><TextViewandroid:id="@+id/custom_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello CustomLayout !"></TextView></com.example.anrdemo.CustomLayout>

代码端对onTouch的事件进行监听:

   TextView customTextView = (TextView)findViewById(R.id.custom_text);customTextView.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {Log.i("test2"," onTouch motionEvent = " + motionEvent);if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL || motionEvent.getAction() == MotionEvent.ACTION_UP ) {//变成了CANCEL后打印一下堆栈,追踪框架哪里进行了改变Log.i("test2"," onTouch ACTION_CANCEL = " ,new Exception());}return true;}});
2021-11-27 13:50:06.911 7010-7010/com.example.anrdemo I/test2:  onTouch ACTION_CANCEL = java.lang.Exceptionat com.example.anrdemo.MainActivity$2.onTouch(MainActivity.java:46)at android.view.View.dispatchTouchEvent(View.java:13949)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)at android.app.Activity.dispatchTouchEvent(Activity.java:4011)at com.example.anrdemo.MainActivity.dispatchTouchEvent(MainActivity.java:72)at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:423)at android.view.View.dispatchPointerEvent(View.java:14212)at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5652)at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5455)at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4958)at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5011)at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4977)at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5117)at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4985)at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5174)at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4958)at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5011)at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4977)at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4985)at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4958)at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7675)at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7644)at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7605)at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7800)at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:188)at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:178)at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7751)at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7824)at android.view.Choreographer$CallbackRecord.run(Choreographer.java:967)at android.view.Choreographer.doCallbacks(Choreographer.java:791)at android.view.Choreographer.doFrame(Choreographer.java:719)at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)at android.os.Handler.handleCallback(Handler.java:883)at android.os.Handler.dispatchMessage(Handler.java:100)

这里就打印出来了框架调用到onTouch中MotionEvent变成CANCEL的流程,方便我们进行源码分析。
具体大家可以自己取追踪,这里我这边列出结果:
base/core/java/android/view/ViewGroup.java

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

这里onInterceptTouchEvent会调用到我们CustomLayout的onInterceptTouchEvent,我们识别到了如果MOVE就return true,所以intercepted这个时候也是true。

final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits))

这里cancelChild就变成了true,所以

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {final boolean handled;final int oldAction = event.getAction();if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {//根据前面cancelChild变成了CANCELevent.setAction(MotionEvent.ACTION_CANCEL);if (child == null) {handled = super.dispatchTouchEvent(event);} else {handled = child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}
//---省略部分
}

从以上可以看出,结合我们Input专题学习知识,我们知道触摸事件InputDispatcher传递给App实际还是ACTION_MOVE,但是app进程ViewGroup的策略把事件变成了ACTION_CANCEL来保证事件的完整性。
总结流程图:

千里马 android framework之MotionEvent.ACTION_CANCEL怎么产生-讨厌的android触摸面试题相关推荐

  1. Android Framework源码阅读计划(2)——LocationManagerService.java

    Android Framework源码阅读计划 Android Framework源码阅读计划(1)--LocationManager.java Android Framework源码阅读计划(2)- ...

  2. 专题总纲目录 Android Framework 总纲

    专题总纲说明: 本系列文章虽说是 Android 的知识体系专题,同时也是学习Android Framework 系统的一个思路,尤其是当我们对Android 框架层 一点都不了解的时候,但前提是要有 ...

  3. Android framework学习

    一:概述 众所周知,Android是一个基于Linux实现的操作系统.但对于Linux内核来说,Android也仅仅只是一个运行在内核之上的应用程序,与其他运行在内核之上的应用程序没有任何区别.所以A ...

  4. 千里马android framework开发解决Accessing hidden method限制,让应用访问隐藏方法(需要可以修改系统源码方案)

    hi,粉丝朋友们大家好! 今天来给大家分享一下,就是经常大家会做安卓系统开发工作问到一个问题,那就是我如果framework代码中增加了一个方法啥的,但是我又不想公开给第三方应用知道,只想让我系统的应 ...

  5. android MotionEvent.ACTION_CANCEL情景分析

    今天在温习ViewGroup的dispatchTouchEvent理解这篇文章的时候,偶然间发现了MotionEvent.ACTION_CANCEL使用的情景,温故而知新,说的就是这了. 还是直入主题 ...

  6. Android事件分发之ACTION_CANCEL机制及作用

    目录 ACTION_CANCEL产生场景 ACTION_CANCEL作用 FLAG_DISALLOW_INTERCEPT的作用 如果要查看ACTION_MOVE与ACTION_UP的事件传递机制,查看 ...

  7. Android Framework 记录

    记录 1.下载源码,目录如下: 2.Android系统的层次如下: 3.项目目录简单分析如下: 4.telphony目录 文件 描述 CellIdentityCdma //描述电信通信标识 CellI ...

  8. Android Framework学习总结

    经过一段时间的学习,对于Android Framework大部分有一定的了解,现在将之前的学习进行总结并分类. 1.Android系统启动相关 Android系统SystemServer启动(上) A ...

  9. 如何调试Android Framework?

    Linus有一句名言广为人知:Read the fucking source code. 但其实,要深入理解某个软件.框架或者系统的工作原理,仅仅「看」代码是远远不够的.就拿Android Frame ...

最新文章

  1. linux的git命令,linux命令实战安装git、配置git、创建仓库、部署项目
  2. ubuntu-11.10-server-i386学习笔记-SVN版本服务器安装
  3. 洛谷P4555 [国家集训队]最长双回文串(manacher 线段树)
  4. Opera视频出海非洲面临的技术挑战及应对
  5. 英伟达显卡bios修改工具_终于可以吃鸡了!英伟达入门级图灵显卡1650S开卖,性能提升25%...
  6. ADAS(3) 各功能模块及解决方案提供商详解
  7. 前端学习(284):纯css实现翻书效果
  8. 四种依恋类型_依恋关系的研究
  9. DotNet操作Excel汇总
  10. ajax跨域获取数据后处理,简单实现ajax获取跨域数据
  11. 文件服务器软件_使用Home FTP Server在电脑之间传文件
  12. html怎么让form弄成一个表,创建一个HTML表,其中每个TR是一个FORM
  13. lnmp mysql 哪个好_[LNMP]Mysql生产环境配置
  14. cutftp.exe
  15. Linux磁盘空间管理利器--ncdu(为你的 系统瘦身)
  16. 数字信号和模拟信号区别
  17. 分析FFMPEG中H264编码流程
  18. 面试题整理 自问自答
  19. 计算机网络-常用英文简写与名词解释
  20. 如何将图片文字转换成文本?

热门文章

  1. 操作系统原理,交互式系统常见的调度算法,时间片轮转RR,虚拟轮转VRR,最高优先级调度,优先级反转问题与应对
  2. 腾讯云物联网-网关设备体验
  3. Python学习-1.基础语法元素
  4. 金字塔pyramid
  5. 微信公众号使用:微信公众平台企业号一次发布多条图文消息的方法
  6. 将网站、网页变成灰白色调的哀悼风格界面的CSS代码
  7. vue 车牌号校验(含新能源)
  8. 如何从Rstudio中导出合适的图片?
  9. 吐血整理深度学习入门路线及导航【教学视频+大神博客+书籍整理】+【资源页】(2019年已经最后一个月了,你还不学深度学习吗???)
  10. 压缩包文件设置了加密怎么解密