此文开头以问答形式进行简单的问题回复,不做深入的原理研究,后面深入源码解答原理机制.
基础
事件发布的对象是谁?
是
Touch
事件
什么是事件列?
从手指接触屏幕 | 手指离开屏幕,这个工程中产生的一系列事件
一般情况下事件列都是以Down事件
开始,UP事件
结束,中间有无数的Move事件
. 此为 ==>事件列
高级
事件分发的本质
将点击事件(
MotionEvent
)传递到某个具体的View
并处理的整个过程 ==> 即 事件传递的过程=分发过程
事件在哪些对象之间进行传递?
Activity
==>ViewGroup
==>View
Android UI界面由Activity、ViewGroup、View
及其派生类组成
事件发布过程由哪些方法协作完成?
dispatchTouchEvent()
- 作用 ==> 分发(传递)点击事件
- 调用时刻 ==> 当点击事件能够传递给当前
View
时,该方法就会被调用onInterceptTouchEvent()
- 作用 -> 处理点击事件
- 调用时刻 -> 在
dispatchTouchEvent()
内部调用onTouchEvent()
- 作用 ==> 判断是否拦截了某个事件(只存在于
ViewGroup
,普通的View
无该方法)- 调用时刻 ==> 在
ViewGroup
的dispatchTouchEvent()
内部调用
分发机制源码解析
Activity事件分发过程
源码讲解 Activity
的事件分发是从 dispatchTouchEvent函数
开始的
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction(); //[1] 处理Down Action触发动作
}
if (getWindow().superDispatchTouchEvent(ev)) { //[2]获取当前顶层window抽象类的PhoneWindow
return true;
}
return onTouchEvent(ev); //[3]当任何视图都没有处理触摸屏事件时调用
}
//实现互动
public void onUserInteraction() {
}
[1] Action.Down动作
触发的事件,在一次事件流程中一定会且执行一次,将方法提供给用户去处理
[2] getWindow().superDispatchTouchEvent(ev)
此方法会调入到Window中
public abstract boolean superDispatchTouchEvent(MotionEvent event);
[ 2.1] Window
本身是一个抽象类,我们进而找到了它的子类PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
[2.1.1] 在子方法实现中我们看到有个 mDecor
它是一个DecorView类型-> 是FrameLayout的子类
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
在DecorView类中我们发现它继续向上调用,调入到了
FrameLayout类
中,通过代码查找我们在FrameLayout类
中并没有找到此方法,而是进入了FrameLayout的父类ViewGroup类
中
[3] 当任何视图都没有处理触摸屏事件时调用 。对于处理窗口之外发生的触摸事件非常有用,例如 处理返回键
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
这里看到有一个
mWindow
它是Window
的实例,进入Window
类中做处理
ViewGroup事件分发过程
public boolean dispatchTouchEvent(MotionEvent ev) {
//mInputEventConsistencyVerifier成员变量是用于调试目的的一致性验证器
if (mInputEventConsistencyVerifier != null) {
///检查触摸事件
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// 正常的事件调度,如果事件的目标是易访问性焦点视图,子视图会处理点击事件
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
//[1]应用安全策略决定是否过滤本次触摸事件
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
//做判定 若actionMasked==ACTION.DOWN
if (actionMasked == MotionEvent.ACTION_DOWN) {
//处理ACTION_DOWN事件,当开始一个新的触摸手势时,丢弃所有以前的状态。
//框架可能已经放弃了前一个手势的up或cancel事件
//由于应用程序切换、ANR或其他状态改变。
//[2]取消和清除所有的触摸标记
cancelAndClearTouchTargets(ev);
//[3]重置所有触摸状态,为新的循环做准备
resetTouchState();
}
// 检查拦截.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//检查不允许拦截成员变量
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//[4]获取是否拦截触摸事件的标记
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // 恢复操作,防止action被更改
} else {
intercepted = false;
}
} else {
// 没有触摸目标,这个动作不是最初的Down
//这个view group继续拦截触摸。
intercepted = true;
}
//如果事件被拦截,则启动正常的事件分发。
//如果已经有处理手势的view,则执行正常的事件分派
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// 检查取消
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
//如果需要,更新指针指向的触摸目标列表.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//不取消&不拦截
if (!canceled && !intercepted) {
//向子视图分发事件,并获取可以获得焦点的视图
...
//[5]清理此指针id的早期触摸目标,防止它们已经不同步了
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
//新触摸标记为null & 子视图总量不为0
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
//[6] 在视图中全面查找,获取所有能够接收该事件的子视图
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//倒序(从底层开始向上)遍历子视图
for (int i = childrenCount - 1; i >= 0; i--) {
//获取经验证后的索引,若 i 大于 childrenCount 则抛出异常 IndexOutOfBoundsException
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
//获取经验证验证后的子视图,
//判定preorderedList 若不为空 则获取preorderedList集合中childIndex索引下的子View,若子View为空 则抛出 RuntimeException异常
//否则将从获取children数组中childIndex索引下的子View,没有子View判null操作
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
if (childWithAccessibilityFocus != null) {
//若子视图无法获取焦点 则退出本次循环
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//若子视图无法接收到指针事件 或 在触摸的点不在view的范围内 则退出本次循环
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
//获取子View的触摸目标
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
//重置取消子view的下一步标记
resetCancelNextUpFlag(child);
//[7] 判定触摸区域是否在子view的区域内, 若判定成功则将事件向下继续分发
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 返回触摸时间
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// 获取子View在mChilden成员数组变量中匹配成功 则获取其的下标索引作为最终的Index下标
//若匹配失败则返回childIndex作为最终的Index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//新增一个TouchTraget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//标记事件已经分发给新的TouchTraget
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
//清除列表数据
if (preorderedList != null) preorderedList.clear();
}
//若找不到子View接收事件,则将其赋给链表中最后添加的newTouchTarget
...
}
}
// 分发Touch target
if (mFirstTouchTarget == null) {
// 子View为null,事件将发给ViewGroup处理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// 继续进行事件分发,排除已发送的触摸标记
...
}
// 手指抬起或出发取消事件后,及时更新targets列表
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;
}
[1] 通过主干的内容的分析我们应该明白事件分发大致流程,那么我们接下来讲几个细节 onFilterTouchEventForSecurity(ev)
如何判定触摸事件是否需要过滤的
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
// 窗口是被掩盖的, 则终止触摸事件.
return false;
}
return true;
}
返回 true 则表示继续分发事件
返回 false 则表示终止过滤掉该事件
[2] 取消和清除所有的触摸标记 cancelAndClearTouchTargets(ev)
private void cancelAndClearTouchTargets(MotionEvent event) {
//判定触摸头部Traget不为Null
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;
}
//遍历 mFirstTouchTarget
for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
//重置mFirstTouchTarget中的元素的view的取消下一步标识
resetCancelNextUpFlag(target.child);
//判定触摸区域是否在子view的区域内, 若判定成功则将事件向下继续分发
dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
}
//清除触摸的所有Targets
clearTouchTargets();
//若是刚创建的取消动作的监听
if (syntheticEvent) {
event.recycle(); //回收该事件
}
}
}
/**
* Clears all touch targets.
*/
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
[3 ] 重置所有触摸状态,为新的循环做准备 resetTouchState();
private void resetTouchState() {
//清除所有触摸标记
clearTouchTargets();
//重置取消下一步的标记
resetCancelNextUpFlag(this);
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
mNestedScrollAxes = SCROLL_AXIS_NONE;
}
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
//使用do循环遍历所有的标记
do {
TouchTarget next = target.next;
target.recycle(); //标记回收
target = next;
} while (target != null);
//最后将mFirstTouchTarget置空
mFirstTouchTarget = null;
}
}
private static boolean resetCancelNextUpFlag(@NonNull View view) {
if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
return true;
}
return false;
}
[4] 获取是否拦截触摸事件的标记 onInterceptTouchEvent(ev);
// 返回 False 表示不拦截; True 表示拦截
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
[5] 清理此指针id的早期触摸目标,防止它们已经不同步了 removePointersFromTouchTargets(idBitsToAssign);
private void removePointersFromTouchTargets(int pointerIdBits) {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//通过while循环 清除mFirstTouchTarget中的元素指针ID
while (target != null) {
final TouchTarget next = target.next;
if ((target.pointerIdBits & pointerIdBits) != 0) {
target.pointerIdBits &= ~pointerIdBits;
if (target.pointerIdBits == 0) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
[6] 在视图中全面查找,获取所有能够接收该事件的子视图 final ArrayList<View> preorderedList = buildTouchDispatchChildList();
public ArrayList<View> buildTouchDispatchChildList() {
return buildOrderedChildList();
}
ArrayList<View> buildOrderedChildList() {
final int childrenCount = mChildrenCount;
if (childrenCount <= 1 || !hasChildWithZ()) return null;
if (mPreSortedChildren == null) {
mPreSortedChildren = new ArrayList<>(childrenCount);
} else {
// 清除调用者,即使不清除也要保证安全
mPreSortedChildren.clear();
mPreSortedChildren.ensureCapacity(childrenCount);
}
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
// 添加一个子元素到链表的末尾
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View nextChild = mChildren[childIndex];
final float currentZ = nextChild.getZ(); //获取子View的Z轴坐标点
int insertIndex = i;
//按照Z轴上的位置 从小到大排列 获取新插入子元素在链表中位置Index
while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
insertIndex--;
}
//将nextChild添加到mPreSortedChildren集合的第inexsetIndex索引下
mPreSortedChildren.add(insertIndex, nextChild);
}
return mPreSortedChildren;
}
[7] 判定触摸区域是否在子view的区域内, 若判定成功则将事件向下继续分发 dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)
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) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// 旧指针ID位和 所需指针ID位进行二进制的与运算,得出结果为0 则表示发生的操作不一致,事件不做处理直接返回false
if (newPointerIdBits == 0) {
return false;
}
//事件指针
final MotionEvent transformedEvent;
//判定指针数量是否一致
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) { //若子View为null
handled = super.dispatchTouchEvent(event); //直接交由View处理,进行事件分发
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
//将事件分发给子View
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
//从现有event事件复制一个新的MotionEvent对象实例
transformedEvent = MotionEvent.obtain(event);
} else {
//事件分割
transformedEvent = event.split(newPointerIdBits);
}
// 做判定 子View若不存在
if (child == null) {
//将事件分发给当前view或GroupView
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
//将事件分发给子View或子GroupView
handled = child.dispatchTouchEvent(transformedEvent);
}
// 完成后,进行回收
transformedEvent.recycle();
//返回handle对象
return handled;
}
View事件分发
public boolean dispatchTouchEvent(MotionEvent event) {
// 若得到的第一个焦点可以被处理.
if (event.isTargetAccessibilityFocus()) {
// 若我们获取的焦点没有实质性的子View,则不进行处理
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// 获取焦点 并进行事件分发.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 停止正在进行的嵌套滚动
stopNestedScroll();
}
//应用安全策略决定是否过滤本次触摸事件
if (onFilterTouchEventForSecurity(event)) {
//如果将事件处理为滚动条拖动,则为true,否则为false
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true; //result 等于 ture 表示事件被消费掉
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true; //表示事件被消费
}
//[1] 若事件没有被消费 & 获取 onTouchEvent函数的处理结果
if (!result && onTouchEvent(event)) {
result = true; //表示事件被消费
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// 处理 抬起事件 或 取消事件 或 (按下 & 没有消费掉)
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
//停止正在进行的嵌套滚动
stopNestedScroll();
}
return result;
}
[1] onTouchEvent(event)函数
处理过程
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX(); //获取 X轴坐标点
final float y = event.getY(); //获取 Y轴坐标点
final int viewFlags = mViewFlags; //view的状态标识
final int action = event.getAction(); //事件的动作
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; //判定是否有效
if ((viewFlags & ENABLED_MASK) == DISABLED) { //若是无效的
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false); //设置视图的状态 为不可触发
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
// 一个禁用的可点击视图仍然会使用触摸事件,它就是不响应它们
return clickable;
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();//处理抬起提示信息
}
if (!clickable) { //若view禁止触发
removeTapCallback(); //移除Tap回调事件
removeLongPressCallback();//移除长按回调事件
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
//调用此函数是为了将焦点放在特定view或其中一个子view上
focusTaken = requestFocus();
}
if (prepressed) {
// 还原按钮被触发前的显示状态
setPressed(true, x, y);
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
//使用Runnable并发布它,而不是调用它 performClick直接
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick(); //执行view的 OnClickListener监听事件
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());//刷新界面显示
} else if (!post(mUnsetPressedState)) {
mUnsetPressedState.run();
}
removeTapCallback();
}
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_DOWN:
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
}
mHasPerformedLongPress = false;
if (!clickable) {
checkForLongClick(0, x, y); //检查长按时间
break;
}
//在触摸期间执行与按钮相关的操作
if (performButtonActionOnTouchDown(event)) {
break;
}
// 遍历层次结构,以确定是否在滚动容器中
boolean isInScrollingContainer = isInScrollingContainer();
// 对于滚动容器内的视图,延迟短时间按下的反馈,以防这是滚动操作.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); //post Tap事件
} else { //不在滚动容器中
//立即显示
setPressed(true, x, y);
checkForLongClick(0, x, y);
}
break;
case MotionEvent.ACTION_CANCEL: //处理取消事件
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break;
case MotionEvent.ACTION_MOVE: //处理移动手势操作
if (clickable) {
drawableHotspotChanged(x, y); //视图触摸热点发生更改变调用此函数
}
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
// Remove any future long press/tap checks
removeTapCallback();
removeLongPressCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
break;
}
return true;
}
return false;
}
总结梳理
前述
经源码分析后感觉流程还是比较复杂,脉络有点不清晰,所以我们对关键主干内容做一下梳理
Activity步骤
- [1]
Activity
的dispatchTouchEvent 函数
向下分发,进入ViewGroup
- [2] 当
ViewGroup
的dispatchTouchEvent函数
不拦截时, 进入最底层的View
的dispatchTouchEvent函数
- [3] 最终交由
View
的OnTouchEvent函数
来处理,若一直不消费 则将回到Activity
的OnTouchEvent函数
处理
ViewGroup步骤
- [4]
ViewGroup
的dispatchTouchEvent 函数
触发,dispatchTouchEvent函数
中包含onInterceptTouchEvent函数
做事件拦截处理- [5] 在分发过程中若
onInterceptTouchEvent函
数进行拦截,则不再进行往下做事件分发,进而交由ViewGroup
的onTouchEvent函数
进行处理- [6]
onTouchEvent函数
由下向上传递,传递过程中任何一层的OnTouchEvent函数
执行消费后,则不会再向上传递,表示当前事件已被消费
View步骤
- [7] 若在
View
的dispatchTochEvent函数
中没有消费ACTION_DOWN事件
,则之后的ACTION_MOVE等事件
不会再接收