Android事件分发源码阅读笔记
时间:2019-02-16
本文章向大家介绍Android事件分发源码阅读笔记,主要包括Android事件分发源码阅读笔记使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
触摸事件流
Down --> Move --> Move --> Up
Down --> Pointer_Down --> Move --> Move --> Pointer_Up --> Up
事件分发流程图
源码注释
ViewGroup.java
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// 我们已经处于这种状态,假设我们的父View也是如此
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// 把它传递给我们的父View
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
//如果事件以无障碍焦点的View为目标,并且此View就是那个无障碍焦点View则开始
//正常事件分发。
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;
// 处理初始的ACTION_DOWN
if (actionMasked == MotionEvent.ACTION_DOWN) {
//开始新的触摸手势时,丢弃(重置)所有先前的状态。
//框架可能已经丢弃了上一个手势的up或cancel事件
//由于应用程序切换,ANR或其他一些状态更改。
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// 检查该ViewGroup是否拦截触摸事件
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {//当触摸事件的Action为ACTION_DOWN,或者mFirstTouchTarget != null(表示子View已经消费了Down事件成为TouchTarget,这个是后续发送给子View的触摸事件(例如Move,Up))
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {// 允许此ViewGroup拦截
// Down事件和发送给TouchTarget的事件都会在这里检测是否拦截
intercepted = onInterceptTouchEvent(ev);
// 恢复Action以防止被更改
ev.setAction(action);
} else {
// 不允许此ViewGroup拦截
intercepted = false;
}
} else {
// 进入这里表明了是Down之后的事件,而TouchTarget为Null,说明子View没有消费之前的Down事件,而是被
// 该ViewGroup拦截消费了。所以后续事件继续被该ViewGroup消费,设置intercepted=true就可以让该ViewGroup的onTouchEvent处理该事件
intercepted = true;
}
//如果该ViewGroup拦截事件,则启动正常事件分发。 同样如果已经存在的一个
//正在处理手势的子View,执行正常的事件分发。
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// 检查 canceled
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// 如果需要,更新pointer down的触摸目标列表。
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
//如果事件的目标是无障碍焦点,如果不处理它,我们会将其提供给
//具有无障碍焦点的View。
//我们清除标志并像往常一样将事件发送给所有子View。
//我们正在查找无障碍焦点宿主以避免保留
//状态,因为这些事件非常罕见。
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(); // 0总是表示Down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
//清除此指针ID的早期触摸目标,以防变得不同步了。
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
//找到可以接收事件的子View。
//从前到后扫描子View。
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);
//如果有一个具有无障碍焦点的View,我们需要它
//首先获取事件并且如果没有处理该事件,我们将执行
//正常分发。
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
//子View已经在其范围内接受了触摸。
//除了正在处理的触摸点之外,还给它新的触摸点(多点触控)。
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 子View想在它的范围内接收触摸事件
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex指向预先排序列表,找到原始索引
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;
}
// 无障碍焦点不处理这个事件,因此清除标志并且对所有的子View进行的正常事件分发
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
//没有找到子View来接收这个事件。
//将指针分配给最近添加的目标。
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// 分发事件到触摸目标
if (mFirstTouchTarget == null) {
// 没有触摸目标,因此将此ViewGroup作为普通View对待.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//发送到触摸目标。如果我们已经发送到这个新的触摸目标,则排除。必要时取消触摸目标。
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 (prtargetedecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// 如果需要,更新指针up或cancel的触摸目标列表。
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;
}
/**
* 将动作事件(MotionEvent)转换到特定子View的坐标空间,
* 过滤掉不相关的指针(每个手指对应一个指针)ID,并在必要时覆盖其操作。
* 如果child为null,则假定MotionEvent将发送给ViewGroup本身。
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
//取消手势(MotionEvent.ACTION_CANCEL)是一种特殊情况。 我们不需要执行任何转换
//或过滤。 重要的是动作,而不是内容。
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;
}
// 计算要传递的指针数。
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;
}
//如果指针的数量相同,并且我们不需要执行任何
//不可逆的转换操作,这时,我们可以重复使用该动作事件进行分发,
//只要我们小心地还原我们所做的任何更改即可。
//否则,我们就需要拷贝一份该动作事件。
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
//child为null,或者child的转换矩阵是单位矩阵。执行完就直接返回了
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);//这里有两种,一种是View的,另一种是ViewGroup的
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
//拷贝动作事件
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// 执行任何必要的转换和分发.
if (child == null) {
//因为子View为null,所以直接分发给自己。super.dispatchTouchEvent对应的是View
//的dispatchTouchEvent方法
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {//child的转换矩阵不是单位矩阵
//通过反向矩阵恢复动作事件
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// 完毕.
transformedEvent.recycle();
return handled;
}
View.java
/**
*将触摸屏动作事件向下传递到目标View,或当此View是目标时,传递到此View
* @param event 要调度的动作事件。
* @return 如果事件由此View处理,则为True,否则为false。
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// 如果事件应首先由可访问性焦点处理
if (event.isTargetAccessibilityFocus()) {
// 我们没有焦点或没有虚拟后代拥有它,不处理这个事件。
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// 我们有焦点并得到了事件,然后使用正常的事件分发
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
// 如果有用于调试目的的一致性验证对象
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
// 获取动作的int值
final int actionMasked = event.getActionMasked();
// 如果动作是按下
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 新手势的防御性清理。停止正在进行的嵌套滚动
stopNestedScroll();
}
//过滤触摸事件以应用安全策略。True表示应该被分发,False表示应该丢弃
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
// 如果这个View没有调用setEnabled(false),这里应该是相等的
&& (mViewFlags & ENABLED_MASK) == ENABLED
//如果调用了setOnTouchListener(),这里就会回调该Listener
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;//说明OnTouchListener.onTouch方法消费了触摸事件
}
//如果事件没有被onTouch方法消费,则传递给View的onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
// 如果事件没有被消费,并且有用于调试目的的一致性验证对象
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
//如果这是手势的结束,则在嵌套滚动后清理;
- 行业研究:大数据(一)
- 控制并发访问的三道屏障: WCF限流(Throttling)体系探秘[下篇]
- 如何通过VPC在本机搭建局域网
- 你常用的10个MySQL命令
- WCF技术剖析之三十一: WCF事务编程[下篇]
- WCF技术剖析之三十一:WCF事务编程[上篇]
- 学习SpringMVC——你们要的REST风格的CRUD来了
- 并发中的同步--WCF并发体系的同步机制实现
- WCF 技术剖析之三十三:你是否了解WCF事务框架体系内部的工作机制?[下篇]
- 学习SpringMVC——从HelloWorld开始
- 小程序年底重磅更新,小游戏上线,最强入口也来了!
- 学习SpringMVC——如何获取请求参数
- 谈谈关于MVP模式中V-P交互问题
- 学习Spring——两个你熟悉的不能再熟悉的场景使用
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Android开发之自定义view实现通讯录列表A~Z字母提示效果【附demo源码下载】
- Android ExpandableRecyclerView使用方法详解
- Android开发之SD卡文件操作分析
- Android学习之Broadcast的简单使用
- Android 仿微信自定义数字键盘的实现代码
- RN在Android打包发布App(详解)
- Android开发之使用SQLite存储数据的方法分析
- Android进度条控件progressbar使用方法详解
- Android自定义view利用Xfermode实现动态文字加载动画
- Android自定义控件实现下拉刷新效果
- Android实现文件解压带进度条功能
- Android使用ViewPager实现图片滑动预览效果
- Android 布局中的android:onClick的使用方法总结
- Android 提交或者上传数据时的dialog弹框动画效果
- Android 调用系统应用的方法总结