本文源码部分的分析是分模块去分析的,最后串联起来的。
API版本28
源码地址:Android os 在线源码
一个Touch事件由一个down事件 + 一个up事件也有可能为0个 + 若干个move事件也可为0个
大致分三种情况:
ACTION_DOWN => ACTION_UP
ACTION_DOWN => ACTION_MOVE => ACTION_MOVE => ..... ACTION_UP
ACTION_DOWN => ACTION_MOVE => ACTION_MOVE => ..... ACTION_CANCEL
用户触摸屏幕会触发调用View.onTouchEvent(MotionEvent event)
产生的一些事件的信息:ACTION_DOWN、ACTION_UP、ACTION_MOVE、坐标等信息,都会作为参数传入MotionEvent对象中。
一系列事件不断产生时,也会不断调用onTouchEvent,onTouchEvent就可以做出不同事件的判断。从而形成触摸反馈!
例:
一个按钮按下就产生ACTION_DOWN
事件(按钮的颜色也会发生改变)、抬起产生ACTION_UP
(按钮颜色恢复)、滑动产生ACTION_MOVE
。
一般按下的时候会产生轻微move ,这可以忽略不计的。
一个Touch事件的产生,分发的结构模型是:树形结构
如图:
1(ViewGroup)
时,会遍历它包含的子view,调用子View的dispatchTouchEvent
进行事件的分发ViewGroup的dispatchTouchEven
t方法,继续重复上述1步骤dispatchTouchEvent函数只负责事件的分发。
如果某一个View消费了事件,那么其他的view就不会消费了,后续的其他move、up事件也都会直接传入,由该view处理。
可以分三大块进分析:
dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
事件分发相关函数dispatchTouchEvent、onTouchEvent
<--- Analyze 1 --->
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
// 分析请看:<--- Analyze 2、 Analyze 3 --->
// getWindow().superDispatchTouchEvent(ev) 返回true
//Activity 的dispatchTouchEvent返回true 分发事件结束
return true;
}
//getWindow().superDispatchTouchEvent(ev) 返回false
// 调用 Activity的onTouchEvent
// onTouchEvent 分析请看:<--- Analyze 4 --->
return onTouchEvent(ev);
}
<--- Analyze 2 PhoneWindow.java --->
//mDecor = DecorView顶层View,间接继承ViewGroup
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
<--- Analyze 3 DecorView.java --->
// DecorView顶层View,间接继承ViewGroup,
// 故:super.dispatchTouchEvent(event);就是调用ViewGroup的dispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
<--- Analyze 4 Activity onTouchEvent --->
// 消费事件未被Activity下的任何view消费
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
// 分析请看 <--- Analyze 5 --->
finish();
return true;
}
return false;
}
<--- Analyze 5 Window.java --->
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
// 触摸事件在边界外返回true 否则false返回
final boolean isOutside =
event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
|| event.getAction() == MotionEvent.ACTION_OUTSIDE;
if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
return true;
}
return false;
}
可根据此流程去过一遍dispatchTouchEvent方法的源码:
此图是核心流程一个大致的流程图,并不是很细,只做引导作用,算是自己的一个看源码的流程。
下图的绘制是按照代码的并列等级
情况去绘制的,不是按照代码、思维逻辑去绘制的,为了方便看源码时对照此图!
等级区分
:如图
有兴趣的可以跟着源码看下:
源码的注释对应上图的一个流程:
版本是28的,源码实在有点多,这里会省略一些代码
源码相关注释:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// .... 省略N行代码
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// step 2 如果是新ACTION_DOWN事件,需要对这次DWON事件之前的事件进行重置
// 1.取消、清空所有触摸事件 2. 重置所有触摸状态
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// step 3 检查是否拦截 如果intercepted=false,就触犯Activity的onTouchEvent
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//step 3 拦截 返回true
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
// .... 省略N行代码
//step 4 没有取消、没有拦截情况
if (!canceled && !intercepted) {
// .... 省略N行代码
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// .... 省略N行代码
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
// .... 省略N行代码
final View[] children = mChildren;
// step 5 遍历找到消费事件的子View
for (int i = childrenCount - 1; i >= 0; i--) {
// .....省略N行代码
// step 6 判断子View是否对事件响应
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// .....省略N行代码
// step 7 将消费的子view添加进事件链
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// .....省略N行代码
}
// .....省略N行代码
}
// .....省略N行代码
}
}
// step 8 判断事件链是否为null
if (mFirstTouchTarget == null) {
//如果事件没有拦截,子View的消费事件会被添加到事件链中,
//mFirstTouchTarget == null 则表明所有的子View没有消费事件 或者触摸点不在任何子view内 或者 当前view将事件拦截了
// step 9
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// 这里当确定down事件后,触发move事件时,就直接找到这里了
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {//step 10遍历事件链找到处理事件的子view
// .....省略N行代码
}
}
// .....省略N行代码
return handled;
}
**注:step 10
:当一个down事件确定后,后续的move事件触发时,就直接找到了这一步,不会在跟前面逻辑一样重新遍历一遍。(TouchTarget的设计时一种责任链模式)
dispatchTransformedTouchEvent 主要负责确定子View是否消费事件
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) {
// 调用 父类的dispatchTouchEvent 即:View的dispatchTouchEvent
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// .... 省略N行代码
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
// 调用 父类的dispatchTouchEvent 即:View的dispatchTouchEvent
handled = super.dispatchTouchEvent(event);
} else {
// .... 省略N行代码
// 调用子View的dispatchTouchEvent
handled = child.dispatchTouchEvent(event);
// .... 省略N行代码
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
// 调用 父类的dispatchTouchEvent 即:View的dispatchTouchEvent
handled = super.dispatchTouchEvent(transformedEvent);
} else {
// .... 省略N行代码
// 调用子View的dispatchTouchEvent
handled = child.dispatchTouchEvent(transformedEvent);
}
// .... 省略N行代码
return handled;
}
public boolean dispatchTouchEvent(MotionEvent event) {
// .....省略N行代码
boolean result = false;
// .....省略N行代码
//step 1. onFilterTouchEventForSecurity 过滤触摸事件:响应触摸事件返回 true 否则false
if (onFilterTouchEventForSecurity(event)) {
//handleScrollBarDragging 滚动条是否消费了事件,消费则result = true
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
// step 2 设置OnTouchListener监听且onTouch返回true ,不在调用 onTouchEvent
//
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViexwFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// step 3 进入onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
// .....省略N行代码
return result;
}
public boolean onTouchEvent(MotionEvent event) {
// ....省略N行代码
//若该控件可点击,则进入switch判断中
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
// 手指抬起
case MotionEvent.ACTION_UP:
// ...省略N行
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent)
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
//performClickInternal 内部调用了performClick()
performClickInternal();
}
}
}
// ...省略N行
break;
// 按下
case MotionEvent.ACTION_DOWN:
// ...省略N行
break;
// 非人为取消事件
case MotionEvent.ACTION_CANCEL:
// .... 省略N行
break;
// 滑动事件
case MotionEvent.ACTION_MOVE:
// .... 省略N行
break;
}
// 若该控件可以点击,则返回true
return true;
}
// 若该控件可以不可点击,则返回false
return false;
}
public boolean performClick() {
// .... 省略N行
final boolean result;
final ListenerInfo li = mListenerInfo;
// 只要调用OnClickListener设置监听,就可以给控件注册点击监听 返回true
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
// .... 省略N行
return result;
}
下图是核心函数的流程图,对应Activity、ViewGroup、View
到这里事件分发已经完成核心的一半
了!
上图只是分析了Activity、ViewGroup、和View分发的大致流程!
下面细节分析子View消费事件 & 不消费事件,具体是怎么传递的!
请回到ViewGroup的源码分析部分:step 6 、 step 7、step 8、 step 9
step 6 、step 7
执行,说明有子View消费了事件,即 true = dispatchTransformedTouchEvent()
//step 6 通过dispatchTransformedTouchEvent找到消费事件的View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// step 7 添加事件链
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
// mFirstTouchTarget 赋值
mFirstTouchTarget = target;
return target;
}
step 8、step 9执行
,则说明 子 view没有消费事件,即 false =dispatchTransformedTouchEvent()
证明上述:
在执行step 7
时,调用addTouchTarget
方法,这里进行了mFirstTouchTarget
的赋值。而满足执行step 7的条件就是子 view消费了事件
如源码中那样,当一个子view消费或不消费,都是以一个boolean值向上传递,再根据此boolean值,确定当前dispatchTouchEvent的返回值的。
如图:
请看 紫色线 和 绿色线
这里它是利用递归模式。我们常说事件分发机制是一个"U型结构"
,应该说是一个伪U型结构
在ViewGroup的dispatchTouchEvent方法中step 4
中代码:
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) { }
这里判断了down事件,所以move事件来临时,直接触发的step 10
到这里事件分发机制就结束了!
以上是我个人对事件分发机制的一种理解,如有不对的地方请指出,会及时更正!