ViewGroup事件分发源码分析
1.AndroidStudio源码调试方式
AndroidStudio默认是支持一部分源码调试的,但是build.gradle(app) 中的sdk版本要保持一致,
最好是编译版本、运行版本以及手机的版本都保持一致,比如
android {
compileSdkVersion 30 //1
buildToolsVersion "30.0.0"
defaultConfig {
applicationId "komine.demos.app"
minSdkVersion 26
targetSdkVersion 30 //2
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
设置断点的源码注意也要一致,比如设置的Android SDK 30,源码也要选择对应的版本.我的AndroidStudio版本是3.6.3
如果都一致发现断点的位置还是不正确,可以重置缓存,在 File-->Invalidate Caches 重新构建.
运行程序之后发现断点上有一个绿色的对勾,表示该断点也可以被执行,并不是100%,所以关键代码调试,最好步进调试.
如果断点成功执行了,如下图

2.ViewGroup事件分发源码分析
当我们在屏幕触摸的时候,不管你触摸到那里,是View上面,还是一个View的没有的Activity上,都会调用Activity的dispatchTouchEvent,如果要观察touchEvent事件分发过程,
可以自己定义一个ViewGroup,和View,在其中打印日志
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<komine.demos.app.TouchLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<komine.demos.app.TouchView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#39c5bb"/>
<komine.demos.app.TouchView
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#39c5bb"/>
</komine.demos.app.TouchLayout>
</LinearLayout>
所有Touch事件都会进到Activity.dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//如果是down事件,则会回调Activity的onUserInteraction()方法
//我们可以在该方法中做一些其他事情
onUserInteraction();
}
//getWindow()是mWindow,在Activity的attach方法中赋值,是一个PhoneWindow对象,是直接new PhoneWindow
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//如果事件没有被消费,则会回调Activity的onTouchEvent()事件
return onTouchEvent(ev);
}
PhoneWindow.superDispatchTouchEvent
如果你想知道mDecor是干什么的或者怎么被创建的,可以看我的另一篇博客
setContentView源码分析
我们直接把它当作ViewGroup看待就行了
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//superDispatchTouchEvent() 其实是调用了ViewGroup的dispatchTouchEvent,
//因为mDecor是继承自ViewGroup
return mDecor.superDispatchTouchEvent(event);
}
重点来了,ViewGroup.dispatchTouchEvent
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
...
//函数的返回值,表示当前事件是否有View消费,true表示有View消费该事件
//则其他View就不能再处理该事件了
boolean handled = false;
//View的方法,默认返回true
if (onFilterTouchEventForSecurity(ev)) {
...
...
//判断当前ViewGroup是否拦截该事件,
final boolean intercepted;
//down事件才判断是否拦截,所以ViewGroup要拦截事件,一定要在
//down事件的时候拦截,其他事件会被忽略
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//FLAG_DISALLOW_INTERCEPT 是在requestDisallowInterceptTouchEvent()中赋值的
//表示子View是否请求了父ViewGroup不拦截本次事件,非ViewGroup子View可以调用getParent().requestDisallowInterceptTouchEvent()
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//如果子View没有请求父ViewGroup不拦截自己
if (!disallowIntercept) {
//调用当前ViewGroup的onInterceptTouchEvent()看看自己是否拦截
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
...
//caceled默认返回false, intercepted表示当前ViewGroup是否要拦截事件
if (!canceled && !intercepted){
...
...
//这是一个倒序循环,最后的那个view会先进行事件分发
for (int i = childrenCount - 1; i >= 0; i--) {
...
...
//见下方,dispatchTransformedTouchEvent()对子view进行事件分发
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//如果有子View返回true,中断事件分发,break完成子View的事件分发
}
}
}
}
//mFirstTouchTarget == null 说明没有子View消费当前事件,则会调用
//父view的dispatchTouchEvent(),完成当前ViewGroup的事件分发
//意思就是,我已经分完了,我的子View没人要消费事件,你看你要不要,把事件分发交给
//父类去处理,然后父类又会重复上述步骤,进行事件分发
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
...
handled = true;
}
//所有ViewGroup分发完成之后的最终结果
return handled;
}
dispatchTransformedTouchEvent
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
...
...
//判断是否是同一根手指
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
//如果子View为空,则返回父类的dispatchTouchEvent()结果
handled = super.dispatchTouchEvent(event);
} else {
...
//调用子View的dispatchTouchEvent(),子View的dispatchTouchEvent()又会调用onTouchEvent(),
//如果View设置了TouchListener则会优先以TouchListener结果返回,然后才会以onTouchEvent()的结果
//作为返回值,这边不贴代码了,可以自己去看看
handled = child.dispatchTouchEvent(event);
...
}
return handled;
}
}
}
Touch事件分发流程:

ViewGroup事件分发源码分析的更多相关文章
- Android View事件分发源码分析
引言 上一篇文章我们介绍了View的事件分发机制,今天我们从源码的角度来学习下事件分发机制. Activity对点击事件的分发过程 事件最先传递给当前Activity,由Activity的dispat ...
- android事件分发源码分析—笔记
昨天晚上从源码角度复习了一下android的事件分发机制,今天将笔记整理下放在网上.其实说复习,也是按着<android开发艺术探索>这本书作者的思路做的笔记. 目录 事件是如何从Acti ...
- Qt中事件分发源码剖析
Qt中事件分发源码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,而且进行分发,这些都是在exec中进行的. 以下举例说明: 1)首先看看以下一段演示样例代码: ...
- 深入理解Spring系列之十:DispatcherServlet请求分发源码分析
转载 https://mp.weixin.qq.com/s/-kEjAeQFBYIGb0zRpST4UQ DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...
- view事件分发源码理解
有些困难无法逃避,没办法,那就只有去解决它.view事件分发对我而言是一块很难啃的骨头,看了<安卓开发艺术探索>关于这个知识点的讲解,看了好几遍,始终不懂,最终通过调试分析结果,看博客,再 ...
- Touch事件分发源码解析
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 以下源码基于Gingerbread 2.3.7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1.先看ViewGroup的di ...
- ApplicationEvent事件机制源码分析
<spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...
- Zepto事件模块源码分析
Zepto事件模块源码分析 一.保存事件数据的handlers 我们知道js原生api中要移除事件,需要传入绑定时的回调函数.而Zepto则可以不传入回调函数,直接移除对应类型的所有事件.原因就在于Z ...
- Tornado 高并发源码分析之五--- IOLoop 对象
IOLoop主要工作 1.将TCPServer 注册到 IOLoop 的事件记到 _handlers 字段,同时注册 READ 和 ERROR 事件到 epoll 2.IOLoop 启动一个大循环,负 ...
随机推荐
- php 正则获取字符串中的汉字(去除字符串中除汉字外的所有字符)
preg_match_all('/[\x{4e00}-\x{9fff}]+/u', $list[$i]['iparr'], $matches); $list[$i]['iparr'] = join(' ...
- 聊聊C#中的composite模式
写在前面 Composite组合模式属于设计模式中比较热门的一个,相信大家对它一定不像对访问者模式那么陌生,毕竟谁又没有遇到过树形结构呢.不过所谓温故而知新,我们还是从一个例子出发,起底一下这个模式吧 ...
- QT与DoNet中单例模式的简单实现
由于使用场景的不同,单例模式的写法也有所区别. 目前接触到的,大多数都是多线程,大量数据处理,还要考虑灵活性,对原有类结构改动最小等因素,所以写法更是多种多样. QT个人较常用的一种写法:(两个文件: ...
- UiPath存在图像Image Exists的介绍和使用
一.Image Exists的介绍 检查是否在指定的UI元素中找到图像,输出的是一个布尔值 二.Image Exists在UiPath中的使用 1. 打开设计器,在设计库中新建一个Sequence,为 ...
- Nginx防御CC攻击
CC攻击可以归为DDoS攻击的一种.他们之间都原理都是一样的,即发送大量的请求数据来导致服务器拒绝服务,是一种连接攻击.CC攻击又可分为代理CC攻击,和肉鸡CC攻击.代理CC攻击是黑客借助代理服务器生 ...
- Codeforces Round #789 (Div. 2)
题集链接 A. Tokitsukaze and All Zero Sequence 题意 Tokitsukaze 有一个长度为 n 的序列 a. 对于每个操作,她选择两个数字 ai 和 aj (i≠j ...
- freeswitch拨打分机号源代码跟踪
概述 freeswitch是一款非常好用的开源VOIP软交换平台. 之前我们有介绍过使用fs拨打分机号的方法,其中代码流程是比较复杂的,所以单独开一章介绍. fs拨打分机号,是使用send_dtmf接 ...
- Leetcode 不同路径系列
Leetcode不同路径系列题解笔记 62. 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 "Start" ). 机器人每次只能向下或者向右移动一 ...
- RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得
前言 首先说一点,企业中最常用的实际上既不是RocketMQ,也不是Kafka,而是RabbitMQ. RocketMQ很强大,但主要是阿里推广自己的云产品而开源出来的一款消息队列,其实中小企业用Ro ...
- SSH 多密钥配置
目录 前言 一.SSH 是什么 二.密钥生成工具 三.密钥类型 四.本地配置 1.单密钥配置 2.多密钥配置 五.远端配置 1.GitHub/Gitee 2.服务器 前言 当我们从 GitHub 克隆 ...