Activity dispatchTouchEvent
1. Activity的dispatchTochEvent作为事件入口
2. 如果事件为ACTION_DOWN(表示一整个触摸事件的开始),则调用onUserInteraction()(空方法),可重写,表示用户的一次交互
3. 通过 getWindow().superDispatchTouchEvent(ev) -> mDecor.superDispatchTouchEvent(event) -> mDecor.dispatchTouchEvent(event) 调用链
将event传递给Activity根View对象(decorView)
4. 如果该事件没被消费,将会调用onTouchEvent方法处理,并返回处理结果:点击事件在Window边界外返回true,一般情况都返回false
相关代码
.
//源码分析:Activity.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
// 一般事件列开始都是DOWN事件 = 按下事件,故此处基本是true
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// a. 该方法为空方法
// b. 当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
onUserInteraction();
}
// a. getWindow() = 获取Window类的对象
// b. Window类是抽象类,其唯一实现类 = PhoneWindow类
// 即此处的Window类对象 = PhoneWindow类对象
// c. Window类的superDispatchTouchEvent() = 1个抽象方法,由子类PhoneWindow类实现
// d. 该方法调用 mDecor.superDispatchTouchEvent(event)
// -> mDecor.dispatchTouchEvent(event)
// e. mDecor = 顶层View (DecorView) 的实例对象
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
// 若getWindow().superDispatchTouchEvent(ev)的返回true
// 则Activity.dispatchTouchEvent()就返回true,则方法结束。
// 即 :该点击事件停止往下传递 & 事件传递过程结束
// 否则:继续往下调用Activity.onTouchEvent
}
// 当事件未被任何view消费时
// 该方法只有在点击事件在Window边界外才会返回true,一般情况都返回false
return onTouchEvent(ev);
}
ViewGroup dispatchTouchEvent
相关代码
//源码分析:ViewGroup.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
...// 仅贴出关键代码
// ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
// 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false)
// 判断值2: !onInterceptTouchEvent(ev) 触发拦截,并返回是否拦截成功
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// 拦截失败或不允许拦截都会进入该代码块
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
// 遍历了当前ViewGroup下的所有子View
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
// 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的View
if (frame.contains(scrolledXInt, scrolledYInt)) {
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
// 条件判断的内部调用了该View的dispatchTouchEvent()
// 实现了点击事件从ViewGroup到子View的传递
if (child.dispatchTouchEvent(ev)) {
mMotionTarget = child;
// 事件被消费,直接返回,结束该事件传递
return true;
}
}
}
}
}
// 拦截成功 ||
// 子View没有消费该事件 || 点击的时空白处(没有任何View接收事件) -> 执行以下逻辑
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
final View target = mMotionTarget;
if (target == null) {
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
// 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
// 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick()
// 即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())
// 此处需与上面区别:子View的dispatchTouchEvent()
return super.dispatchTouchEvent(ev);
}
...
}
View dispatchTouchEvent
相关代码
//源码分析:View.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent event) {
// 设置了TouchListener && 当前View可用 &&
// TouchListener.onTouch()返回 true
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
//返回 true 消费该事件
return true;
}
// 该方法 ->> View 可点击返回 true 其他返回 false
// 处理View的触摸事件,会调用
// performClickInternal() -> performClick() ->
// OnClickListener.onClick()
return onTouchEvent(event);
}